1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.settings.localepicker; 18 19 import android.content.Context; 20 import android.os.Bundle; 21 import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; 22 import android.support.v7.widget.LinearLayoutManager; 23 import android.support.v7.widget.RecyclerView; 24 import android.view.View; 25 26 import com.android.settings.R; 27 28 /** 29 * Add accessibility actions to the drag-and-drop locale list 30 * 31 * <p>Dragging is not supported neither by TalkBack or the accessibility 32 * framework at the moment. So we need custom actions to be able 33 * to change the order of the locales.</p> 34 * 35 * <p>Also, the remove functionality is difficult to discover and use 36 * with TalkBack only, so we are also adding a "remove" action.</p> 37 * 38 * <p>It only removes one locale at the time, but most users don't 39 * really add many locales "by mistake", so there is no real need 40 * to delete a lot of locales at once.</p> 41 */ 42 public class LocaleLinearLayoutManager extends LinearLayoutManager { 43 private final LocaleDragAndDropAdapter mAdapter; 44 private final Context mContext; 45 46 private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveUp; 47 private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveDown; 48 private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveTop; 49 private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionMoveBottom; 50 private final AccessibilityNodeInfoCompat.AccessibilityActionCompat mActionRemove; 51 LocaleLinearLayoutManager(Context context, LocaleDragAndDropAdapter adapter)52 public LocaleLinearLayoutManager(Context context, LocaleDragAndDropAdapter adapter) { 53 super(context); 54 this.mContext = context; 55 this.mAdapter = adapter; 56 57 this.mActionMoveUp = new AccessibilityNodeInfoCompat.AccessibilityActionCompat( 58 R.id.action_drag_move_up, 59 mContext.getString(R.string.action_drag_label_move_up)); 60 this.mActionMoveDown = new AccessibilityNodeInfoCompat.AccessibilityActionCompat( 61 R.id.action_drag_move_down, 62 mContext.getString(R.string.action_drag_label_move_down)); 63 this.mActionMoveTop = new AccessibilityNodeInfoCompat.AccessibilityActionCompat( 64 R.id.action_drag_move_top, 65 mContext.getString(R.string.action_drag_label_move_top)); 66 this.mActionMoveBottom = new AccessibilityNodeInfoCompat.AccessibilityActionCompat( 67 R.id.action_drag_move_bottom, 68 mContext.getString(R.string.action_drag_label_move_bottom)); 69 this.mActionRemove = new AccessibilityNodeInfoCompat.AccessibilityActionCompat( 70 R.id.action_drag_remove, 71 mContext.getString(R.string.action_drag_label_remove)); 72 } 73 74 @Override onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, RecyclerView.State state, View host, AccessibilityNodeInfoCompat info)75 public void onInitializeAccessibilityNodeInfoForItem(RecyclerView.Recycler recycler, 76 RecyclerView.State state, View host, AccessibilityNodeInfoCompat info) { 77 78 super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info); 79 80 final int itemCount = this.getItemCount(); 81 final int position = this.getPosition(host); 82 final LocaleDragCell dragCell = (LocaleDragCell) host; 83 84 // We want the description to be something not localizable, so that any TTS engine for 85 // any language can handle it. And we want the position to be part of it. 86 // So we use something like "2, French (France)" 87 final String description = 88 (position + 1) + ", " + dragCell.getCheckbox().getContentDescription(); 89 info.setContentDescription(description); 90 91 if (mAdapter.isRemoveMode()) { // We don't move things around in remove mode 92 return; 93 } 94 95 // The order in which we add the actions is important for the circular selection menu. 96 // With the current order the "up" action will be (more or less) up, and "down" more 97 // or less down ("more or less" because we have 5 actions) 98 if (position > 0) { // it is not the first one 99 info.addAction(mActionMoveUp); 100 info.addAction(mActionMoveTop); 101 } 102 if (position + 1 < itemCount) { // it is not the last one 103 info.addAction(mActionMoveDown); 104 info.addAction(mActionMoveBottom); 105 } 106 if (itemCount > 1) { 107 info.addAction(mActionRemove); 108 } 109 } 110 111 @Override performAccessibilityActionForItem(RecyclerView.Recycler recycler, RecyclerView.State state, View host, int action, Bundle args)112 public boolean performAccessibilityActionForItem(RecyclerView.Recycler recycler, 113 RecyclerView.State state, View host, int action, Bundle args) { 114 115 final int itemCount = this.getItemCount(); 116 final int position = this.getPosition(host); 117 boolean result = false; 118 119 switch (action) { 120 case R.id.action_drag_move_up: 121 if (position > 0) { 122 mAdapter.onItemMove(position, position - 1); 123 result = true; 124 } 125 break; 126 case R.id.action_drag_move_down: 127 if (position + 1 < itemCount) { 128 mAdapter.onItemMove(position, position + 1); 129 result = true; 130 } 131 break; 132 case R.id.action_drag_move_top: 133 if (position != 0) { 134 mAdapter.onItemMove(position, 0); 135 result = true; 136 } 137 break; 138 case R.id.action_drag_move_bottom: 139 if (position != itemCount - 1) { 140 mAdapter.onItemMove(position, itemCount - 1); 141 result = true; 142 } 143 break; 144 case R.id.action_drag_remove: 145 if (itemCount > 1) { 146 mAdapter.removeItem(position); 147 result = true; 148 } 149 break; 150 default: 151 return super.performAccessibilityActionForItem(recycler, state, host, action, args); 152 } 153 154 if (result) { 155 mAdapter.doTheUpdate(); 156 } 157 return result; 158 } 159 } 160