platform-packages-apps-Settings / src / com / android / settings / localepicker / LocaleLinearLayoutManager.java
LocaleLinearLayoutManager.java
Raw
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings.localepicker;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.android.settings.R;

/**
 * Add accessibility actions to the drag-and-drop locale list
 *
 * <p>Dragging is not supported neither by TalkBack or the accessibility
 * framework at the moment. So we need custom actions to be able
 * to change the order of the locales.</p>
 *
 * <p>Also, the remove functionality is difficult to discover and use
 * with TalkBack only, so we are also adding a "remove" action.</p>
 *
 * <p>It only removes one locale at the time, but most users don't
 * really add many locales "by mistake", so there is no real need
 * to delete a lot of locales at once.</p>
 */
public class LocaleLinearLayoutManager extends LinearLayoutManager {
    private final LocaleDragAndDropAdapter mAdapter;
    private final Context mContext;

    private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveUp;
    private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveDown;
    private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveTop;
    private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveBottom;
    private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionRemove;

    public LocaleLinearLayoutManager(Context context, LocaleDragAndDropAdapter adapter) {
        super(context);
        this.mContext = context;
        this.mAdapter = adapter;

        this.mActionMoveUp = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                R.id.action_drag_move_up,
                mContext.getString(R.string.action_drag_label_move_up));
        this.mActionMoveDown = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                R.id.action_drag_move_down,
                mContext.getString(R.string.action_drag_label_move_down));
        this.mActionMoveTop = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                R.id.action_drag_move_top,
                mContext.getString(R.string.action_drag_label_move_top));
        this.mActionMoveBottom = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                R.id.action_drag_move_bottom,
                mContext.getString(R.string.action_drag_label_move_bottom));
        this.mActionRemove = new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                R.id.action_drag_remove,
                mContext.getString(R.string.action_drag_label_remove));
    }

    @Override
    public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler,
            RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) {

        super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);

        final int itemCount = this.getItemCount();
        final int position = this.getPosition(host);
        final LocaleDragCell dragCell = (LocaleDragCell) host;

        // We want the description to be something not localizable, so that any TTS engine for
        // any language can handle it. And we want the position to be part of it.
        // So we use something like "2, French (France)"
        final String description =
                (position + 1) + ", " + dragCell.getCheckbox().getContentDescription();
        info.setContentDescription(description);

        if (mAdapter.isRemoveMode()) { // We don't move things around in remove mode
            return;
        }

        // The order in which we add the actions is important for the circular selection menu.
        // With the current order the "up" action will be (more or less) up, and "down" more
        // or less down ("more or less" because we have 5 actions)
        if (position > 0) { // it is not the first one
            info.addAction(mActionMoveUp);
            info.addAction(mActionMoveTop);
        }
        if (position + 1 < itemCount) { // it is not the last one
            info.addAction(mActionMoveDown);
            info.addAction(mActionMoveBottom);
        }
        if (itemCount > 1) {
            info.addAction(mActionRemove);
        }
    }

    @Override
    public boolean performAccessibilityActionForItem(RecyclerView.Recycler recycler,
            RecyclerView.State state, View host, int action, Bundle args) {

        final int itemCount = this.getItemCount();
        final int position = this.getPosition(host);
        boolean result = false;

        switch (action) {
            case R.id.action_drag_move_up:
                if (position > 0) {
                    mAdapter.onItemMove(position, position - 1);
                    result = true;
                }
                break;
            case R.id.action_drag_move_down:
                if (position + 1 < itemCount) {
                    mAdapter.onItemMove(position, position + 1);
                    result = true;
                }
                break;
            case R.id.action_drag_move_top:
                if (position != 0) {
                    mAdapter.onItemMove(position, 0);
                    result = true;
                }
                break;
            case R.id.action_drag_move_bottom:
                if (position != itemCount - 1) {
                    mAdapter.onItemMove(position, itemCount - 1);
                    result = true;
                }
                break;
            case R.id.action_drag_remove:
                if (itemCount > 1) {
                    mAdapter.removeItem(position);
                    result = true;
                }
                break;
            default:
                return super.performAccessibilityActionForItem(recycler, state, host, action, args);
        }

        if (result) {
            mAdapter.doTheUpdate();
        }
        return result;
    }
}