Home | History | Annotate | Download | only in localepicker
      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 
     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
     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
    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