/* * 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; } }