Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac22e5bf authored by Fabian Kozynski's avatar Fabian Kozynski
Browse files

Add TileAdapterDelegate

Use an AccessibilityDelegate for the actions in QSCustomizer. Adding or
removing a tile can now be done with the click action, whereas moving or
adding to a particular position require a context action.

This removes the old custom dialog and improves the overall
accessibility.

Test: manual
Test: atest TileAdapterDelegate
Bug: 168039987
Bug: 140366995

Change-Id: Ib5b19aeebb54c46573555563c3f39bd922b68896
parent 5f028c99
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -170,6 +170,9 @@
    <item type="id" name="accessibility_action_controls_move_before" />
    <item type="id" name="accessibility_action_controls_move_after" />

    <item type="id" name="accessibility_action_qs_move_to_position" />
    <item type="id" name="accessibility_action_qs_add_to_position" />

    <!-- Accessibility actions for PIP -->
    <item type="id" name="action_pip_resize" />
</resources>
+15 −12
Original line number Diff line number Diff line
@@ -2260,23 +2260,26 @@
    <!-- SysUI Tuner: Other section -->
    <string name="other">Other</string>

    <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_label">Position <xliff:g id="position" example="2">%1$d</xliff:g>, <xliff:g id="tile_name" example="Wi-Fi">%2$s</xliff:g>. Double tap to edit.</string>
    <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_remove_tile_action">remove tile</string>

    <!-- Accessibility description of a QS tile while editing positions [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_add_tile_label"><xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g>. Double tap to add.</string>
    <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to end" in screen readers [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_add_action">add tile to end</string>

    <!-- Accessibility description of option to move QS tile [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_move_tile">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
    <!-- Accessibility action for context menu to move QS tile [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_start_move">Move tile</string>

    <!-- Accessibility description of option to remove QS tile [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_remove_tile">Remove <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g></string>
    <!-- Accessibility action for context menu to add QS tile [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_start_add">Add tile</string>

    <!-- Accessibility action when QS tile is to be added [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_add">Add <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
    <!-- Accessibility description when QS tile is to be moved, indicating the destination position [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_move_to_position">Move to <xliff:g id="position" example="5">%1$d</xliff:g></string>

    <!-- Accessibility action when QS tile is to be moved [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_move">Move <xliff:g id="tile_name" example="Wi-Fi">%1$s</xliff:g> to position <xliff:g id="position" example="5">%2$d</xliff:g></string>
    <!-- Accessibility description when QS tile is to be added, indicating the destination position [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_tile_add_to_position">Add to position <xliff:g id="position" example="5">%1$d</xliff:g></string>

    <!-- Accessibility description indicating the currently selected tile's position. Only used for tiles that are currently in use [CHAR LIMIT=NONE] -->
    <string name="accessibility_qs_edit_position">Position <xliff:g id="position" example="5">%1$d</xliff:g></string>

    <!-- Accessibility label for window when QS editing is happening [CHAR LIMIT=NONE] -->
    <string name="accessibility_desc_quick_settings_edit">Quick settings editor.</string>
+133 −81
Original line number Diff line number Diff line
@@ -14,11 +14,8 @@

package com.android.systemui.qs.customize;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
@@ -28,10 +25,11 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup;
import androidx.recyclerview.widget.ItemTouchHelper;
@@ -49,7 +47,6 @@ import com.android.systemui.qs.customize.TileQueryHelper.TileInfo;
import com.android.systemui.qs.customize.TileQueryHelper.TileStateListener;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.statusbar.phone.SystemUIDialog;

import java.util.ArrayList;
import java.util.List;
@@ -78,10 +75,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
    private final List<TileInfo> mTiles = new ArrayList<>();
    private final ItemTouchHelper mItemTouchHelper;
    private final ItemDecoration mDecoration;
    private final AccessibilityManager mAccessibilityManager;
    private final int mMinNumTiles;
    private int mEditIndex;
    private int mTileDividerIndex;
    private int mFocusIndex;
    private boolean mNeedsFocus;
    private List<String> mCurrentSpecs;
    private List<TileInfo> mOtherTiles;
@@ -90,17 +87,28 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
    private Holder mCurrentDrag;
    private int mAccessibilityAction = ACTION_NONE;
    private int mAccessibilityFromIndex;
    private CharSequence mAccessibilityFromLabel;
    private QSTileHost mHost;
    private final UiEventLogger mUiEventLogger;
    private final AccessibilityDelegateCompat mAccessibilityDelegate;
    private RecyclerView mRecyclerView;

    public TileAdapter(Context context, UiEventLogger uiEventLogger) {
        mContext = context;
        mUiEventLogger = uiEventLogger;
        mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
        mItemTouchHelper = new ItemTouchHelper(mCallbacks);
        mDecoration = new TileItemDecoration(context);
        mMinNumTiles = context.getResources().getInteger(R.integer.quick_settings_min_num_tiles);
        mAccessibilityDelegate = new TileAdapterDelegate();
    }

    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = recyclerView;
    }

    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        mRecyclerView = null;
    }

    public void setHost(QSTileHost host) {
@@ -130,7 +138,6 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
            // Remove blank tile from last spot
            mTiles.remove(--mEditIndex);
            // Update the tile divider position
            mTileDividerIndex--;
            notifyDataSetChanged();
        }
        mAccessibilityAction = ACTION_NONE;
@@ -241,7 +248,6 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
    }

    private void setSelectableForHeaders(View view) {
        if (mAccessibilityManager.isTouchExplorationEnabled()) {
        final boolean selectable = mAccessibilityAction == ACTION_NONE;
        view.setFocusable(selectable);
        view.setImportantForAccessibility(selectable
@@ -249,7 +255,6 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
                : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
        view.setFocusableInTouchMode(selectable);
    }
    }

    @Override
    public void onBindViewHolder(final Holder holder, int position) {
@@ -285,12 +290,11 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
            holder.mTileView.setVisibility(View.VISIBLE);
            holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
            holder.mTileView.setContentDescription(mContext.getString(
                    R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel,
                    position));
                    R.string.accessibility_qs_edit_tile_add_to_position, position));
            holder.mTileView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    selectPosition(holder.getAdapterPosition(), v);
                    selectPosition(holder.getLayoutPosition());
                }
            });
            focusOnHolder(holder);
@@ -299,53 +303,48 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta

        TileInfo info = mTiles.get(position);

        if (position > mEditIndex) {
        final boolean selectable = 0 < position && position < mEditIndex;
        if (selectable && mAccessibilityAction == ACTION_ADD) {
            info.state.contentDescription = mContext.getString(
                    R.string.accessibility_qs_edit_add_tile_label, info.state.label);
        } else if (mAccessibilityAction == ACTION_ADD) {
                    R.string.accessibility_qs_edit_tile_add_to_position, position);
        } else if (selectable && mAccessibilityAction == ACTION_MOVE) {
            info.state.contentDescription = mContext.getString(
                    R.string.accessibility_qs_edit_tile_add, mAccessibilityFromLabel, position);
        } else if (mAccessibilityAction == ACTION_MOVE) {
            info.state.contentDescription = mContext.getString(
                    R.string.accessibility_qs_edit_tile_move, mAccessibilityFromLabel, position);
                    R.string.accessibility_qs_edit_tile_move_to_position, position);
        } else {
            info.state.contentDescription = mContext.getString(
                    R.string.accessibility_qs_edit_tile_label, position, info.state.label);
            info.state.contentDescription = info.state.label;
        }
        info.state.expandedAccessibilityClassName = "";

        holder.mTileView.handleStateChanged(info.state);
        holder.mTileView.setShowAppLabel(position > mEditIndex && !info.isSystem);
        holder.mTileView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
        holder.mTileView.setClickable(true);
        holder.mTileView.setOnClickListener(null);
        holder.mTileView.setFocusable(true);
        holder.mTileView.setFocusableInTouchMode(true);

        if (mAccessibilityManager.isTouchExplorationEnabled()) {
            final boolean selectable = mAccessibilityAction == ACTION_NONE || position < mEditIndex;
        if (mAccessibilityAction != ACTION_NONE) {
            holder.mTileView.setClickable(selectable);
            holder.mTileView.setFocusable(selectable);
            holder.mTileView.setFocusableInTouchMode(selectable);
            holder.mTileView.setImportantForAccessibility(selectable
                    ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
                    : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
            holder.mTileView.setFocusableInTouchMode(selectable);
            if (selectable) {
                holder.mTileView.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int position = holder.getAdapterPosition();
                        int position = holder.getLayoutPosition();
                        if (position == RecyclerView.NO_POSITION) return;
                        if (mAccessibilityAction != ACTION_NONE) {
                            selectPosition(position, v);
                        } else {
                            if (position < mEditIndex && canRemoveTiles()) {
                                showAccessibilityDialog(position, v);
                            } else if (position < mEditIndex && !canRemoveTiles()) {
                                startAccessibleMove(position);
                            } else {
                                startAccessibleAdd(position);
                            }
                            selectPosition(position);
                        }
                    }
                });
                if (position == mAccessibilityFromIndex) {
                    focusOnHolder(holder);
            }
        }
        if (position == mFocusIndex) {
            focusOnHolder(holder);
        }
    }

@@ -360,9 +359,13 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
                        int oldLeft, int oldTop, int oldRight, int oldBottom) {
                    holder.mTileView.removeOnLayoutChangeListener(this);
                    holder.mTileView.requestFocus();
                    if (mAccessibilityAction == ACTION_NONE) {
                        holder.mTileView.clearFocus();
                    }
                }
            });
            mNeedsFocus = false;
            mFocusIndex = RecyclerView.NO_POSITION;
        }
    }

@@ -370,72 +373,77 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
        return mCurrentSpecs.size() > mMinNumTiles;
    }

    private void selectPosition(int position, View v) {
    private void selectPosition(int position) {
        if (mAccessibilityAction == ACTION_ADD) {
            // Remove the placeholder.
            mTiles.remove(mEditIndex--);
            notifyItemRemoved(mEditIndex);
        }
        mAccessibilityAction = ACTION_NONE;
        move(mAccessibilityFromIndex, position, v);
        notifyDataSetChanged();
    }

    private void showAccessibilityDialog(final int position, final View v) {
        final TileInfo info = mTiles.get(position);
        CharSequence[] options = new CharSequence[] {
                mContext.getString(R.string.accessibility_qs_edit_move_tile, info.state.label),
                mContext.getString(R.string.accessibility_qs_edit_remove_tile, info.state.label),
        };
        AlertDialog dialog = new Builder(mContext)
                .setItems(options, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (which == 0) {
                            startAccessibleMove(position);
                        } else {
                            move(position, info.isSystem ? mEditIndex : mTileDividerIndex, v);
                            notifyItemChanged(mTileDividerIndex);
        move(mAccessibilityFromIndex, position, false);
        mFocusIndex = position;
        mNeedsFocus = true;
        notifyDataSetChanged();
    }
                    }
                }).setNegativeButton(android.R.string.cancel, null)
                .create();
        SystemUIDialog.setShowForAllUsers(dialog, true);
        SystemUIDialog.applyFlags(dialog);
        dialog.show();
    }

    private void startAccessibleAdd(int position) {
        mAccessibilityFromIndex = position;
        mAccessibilityFromLabel = mTiles.get(position).state.label;
        mAccessibilityAction = ACTION_ADD;
        // Add placeholder for last slot.
        mTiles.add(mEditIndex++, null);
        // Update the tile divider position
        mTileDividerIndex++;
        mFocusIndex = mEditIndex - 1;
        mNeedsFocus = true;
        if (mRecyclerView != null) {
            mRecyclerView.post(() -> mRecyclerView.smoothScrollToPosition(mFocusIndex));
        }
        notifyDataSetChanged();
    }

    private void startAccessibleMove(int position) {
        mAccessibilityFromIndex = position;
        mAccessibilityFromLabel = mTiles.get(position).state.label;
        mAccessibilityAction = ACTION_MOVE;
        mFocusIndex = position;
        mNeedsFocus = true;
        notifyDataSetChanged();
    }

    private boolean canRemoveFromPosition(int position) {
        return canRemoveTiles() && isCurrentTile(position);
    }

    private boolean isCurrentTile(int position) {
        return position < mEditIndex;
    }

    private boolean canAddFromPosition(int position) {
        return position > mEditIndex;
    }

    private void addFromPosition(int position) {
        if (!canAddFromPosition(position)) return;
        move(position, mEditIndex);
    }

    private void removeFromPosition(int position) {
        if (!canRemoveFromPosition(position)) return;
        TileInfo info = mTiles.get(position);
        move(position, info.isSystem ? mEditIndex : mTileDividerIndex);
    }

    public SpanSizeLookup getSizeLookup() {
        return mSizeLookup;
    }

    private boolean move(int from, int to, View v) {
    private boolean move(int from, int to) {
        return move(from, to, true);
    }

    private boolean move(int from, int to, boolean notify) {
        if (to == from) {
            return true;
        }
        CharSequence fromLabel = mTiles.get(from).state.label;
        move(from, to, mTiles);
        move(from, to, mTiles, notify);
        updateDividerLocations();
        if (to >= mEditIndex) {
            mUiEventLogger.log(QSEditEvent.QS_EDIT_REMOVE, 0, strip(mTiles.get(to)));
@@ -477,10 +485,12 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
        return spec;
    }

    private <T> void move(int from, int to, List<T> list) {
    private <T> void move(int from, int to, List<T> list, boolean notify) {
        list.add(to, list.remove(from));
        if (notify) {
            notifyItemMoved(from, to);
        }
    }

    public class Holder extends ViewHolder {
        private CustomizeTileView mTileView;
@@ -491,6 +501,8 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
                mTileView = (CustomizeTileView) ((FrameLayout) itemView).getChildAt(0);
                mTileView.setBackground(null);
                mTileView.getIcon().disableAnimation();
                mTileView.setTag(this);
                ViewCompat.setAccessibilityDelegate(mTileView, mAccessibilityDelegate);
            }
        }

@@ -527,6 +539,46 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
                    .setDuration(DRAG_LENGTH)
                    .alpha(.6f);
        }

        boolean canRemove() {
            return canRemoveFromPosition(getLayoutPosition());
        }

        boolean canAdd() {
            return canAddFromPosition(getLayoutPosition());
        }

        void toggleState() {
            if (canAdd()) {
                add();
            } else {
                remove();
            }
        }

        private void add() {
            addFromPosition(getLayoutPosition());
        }

        private void remove() {
            removeFromPosition(getLayoutPosition());
        }

        boolean isCurrentTile() {
            return TileAdapter.this.isCurrentTile(getLayoutPosition());
        }

        void startAccessibleAdd() {
            TileAdapter.this.startAccessibleAdd(getLayoutPosition());
        }

        void startAccessibleMove() {
            TileAdapter.this.startAccessibleMove(getLayoutPosition());
        }

        boolean canTakeAccessibleAction() {
            return mAccessibilityAction == ACTION_NONE;
        }
    }

    private final SpanSizeLookup mSizeLookup = new SpanSizeLookup() {
@@ -648,7 +700,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
                    to == 0 || to == RecyclerView.NO_POSITION) {
                return false;
            }
            return move(from, to, target.itemView);
            return move(from, to);
        }

        @Override
+152 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.systemui.qs.customize;

import android.os.Bundle;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;

import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;

import com.android.systemui.R;

import java.util.List;

/**
 * Accessibility delegate for {@link TileAdapter} views.
 *
 * This delegate will populate the accessibility info with the proper actions that can be taken for
 * the different tiles:
 * <ul>
 *   <li>Add to end if the tile is not a current tile (by double tap).</li>
 *   <li>Add to a given position (by context menu). This will let the user select a position.</li>
 *   <li>Remove, if the tile is a current tile (by double tap).</li>
 *   <li>Move to a given position (by context menu). This will let the user select a position.</li>
 * </ul>
 *
 * This only handles generating the associated actions. The logic for selecting positions is handled
 * by {@link TileAdapter}.
 *
 * In order for the delegate to work properly, the asociated {@link TileAdapter.Holder} should be
 * passed along with the view using {@link View#setTag}.
 */
class TileAdapterDelegate extends AccessibilityDelegateCompat {

    private static final int MOVE_TO_POSITION_ID = R.id.accessibility_action_qs_move_to_position;
    private static final int ADD_TO_POSITION_ID = R.id.accessibility_action_qs_add_to_position;

    private TileAdapter.Holder getHolder(View view) {
        return (TileAdapter.Holder) view.getTag();
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
        super.onInitializeAccessibilityNodeInfo(host, info);
        TileAdapter.Holder holder = getHolder(host);
        info.setCollectionItemInfo(null);
        info.setStateDescription("");
        if (holder == null || !holder.canTakeAccessibleAction()) {
            // If there's not a holder (not a regular Tile) or an action cannot be taken
            // because we are in the middle of an accessibility action, don't create a special node.
            return;
        }

        addClickAction(host, info, holder);
        maybeAddActionAddToPosition(host, info, holder);
        maybeAddActionMoveToPosition(host, info, holder);

        if (holder.isCurrentTile()) {
            info.setStateDescription(host.getContext().getString(
                    R.string.accessibility_qs_edit_position, holder.getLayoutPosition()));
        }
    }

    @Override
    public boolean performAccessibilityAction(View host, int action, Bundle args) {
        TileAdapter.Holder holder = getHolder(host);

        if (holder == null || !holder.canTakeAccessibleAction()) {
            // If there's not a holder (not a regular Tile) or an action cannot be taken
            // because we are in the middle of an accessibility action, perform the default action.
            return super.performAccessibilityAction(host, action, args);
        }
        if (action == AccessibilityNodeInfo.ACTION_CLICK) {
            holder.toggleState();
            return true;
        } else if (action == MOVE_TO_POSITION_ID) {
            holder.startAccessibleMove();
            return true;
        } else if (action == ADD_TO_POSITION_ID) {
            holder.startAccessibleAdd();
            return true;
        } else {
            return super.performAccessibilityAction(host, action, args);
        }
    }

    private void addClickAction(
            View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
        String clickActionString;
        if (holder.canAdd()) {
            clickActionString = host.getContext().getString(
                    R.string.accessibility_qs_edit_tile_add_action);
        } else if (holder.canRemove()) {
            clickActionString = host.getContext().getString(
                    R.string.accessibility_qs_edit_remove_tile_action);
        } else {
            // Remove the default click action if tile can't either be added or removed (for example
            // if there's the minimum number of tiles)
            List<AccessibilityNodeInfoCompat.AccessibilityActionCompat> listOfActions =
                    info.getActionList(); // This is a copy
            int numActions = listOfActions.size();
            for (int i = 0; i < numActions; i++) {
                if (listOfActions.get(i).getId() == AccessibilityNodeInfo.ACTION_CLICK) {
                    info.removeAction(listOfActions.get(i));
                }
            }
            return;
        }

        AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
                new AccessibilityNodeInfoCompat.AccessibilityActionCompat(
                        AccessibilityNodeInfo.ACTION_CLICK, clickActionString);
        info.addAction(action);
    }

    private void maybeAddActionMoveToPosition(
            View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
        if (holder.isCurrentTile()) {
            AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
                    new AccessibilityNodeInfoCompat.AccessibilityActionCompat(MOVE_TO_POSITION_ID,
                            host.getContext().getString(
                                    R.string.accessibility_qs_edit_tile_start_move));
            info.addAction(action);
        }
    }

    private void maybeAddActionAddToPosition(
            View host, AccessibilityNodeInfoCompat info, TileAdapter.Holder holder) {
        if (holder.canAdd()) {
            AccessibilityNodeInfoCompat.AccessibilityActionCompat action =
                    new AccessibilityNodeInfoCompat.AccessibilityActionCompat(ADD_TO_POSITION_ID,
                            host.getContext().getString(
                                    R.string.accessibility_qs_edit_tile_start_add));
            info.addAction(action);
        }
    }
}
+265 −0

File added.

Preview size limit exceeded, changes collapsed.