From f957244b889faafc23d6bd03f5f1f46f43527a09 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Thu, 18 Nov 2021 17:42:35 -0800 Subject: [PATCH] Show multiple App Info A11y options for split app icons * AccessibilityNode actions are required to have a unique resourceId to show as an action in A11y dialog. For now, only AppInfo option is shown for each app in split, but moving forward we'll need to add resourceIDs for each option that can show up for either app. Fixes: 200609838 Test: Saw multiple options for App Info for each app come up in talkback mode Change-Id: I92b349347354ac639537021d775eea814c866a0e --- .../quickstep/TaskShortcutFactory.java | 8 ++- .../com/android/quickstep/views/TaskView.java | 39 ++++++++++----- res/values/id.xml | 4 ++ res/values/strings.xml | 7 +-- .../launcher3/popup/SystemShortcut.java | 49 ++++++++++++++++++- 5 files changed, 90 insertions(+), 17 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java index 8c4ba97c07..cbdbdb5571 100644 --- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java +++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java @@ -72,7 +72,13 @@ public interface TaskShortcutFactory { @Override public SystemShortcut getShortcut(BaseDraggingActivity activity, TaskIdAttributeContainer taskContainer) { - return new AppInfo(activity, taskContainer.getItemInfo()); + TaskView taskView = taskContainer.getTaskView(); + AppInfo.SplitAccessibilityInfo accessibilityInfo = + new AppInfo.SplitAccessibilityInfo(taskView.containsMultipleTasks(), + TaskUtils.getTitle(taskView.getContext(), taskContainer.getTask()), + taskContainer.getA11yNodeId() + ); + return new AppInfo(activity, taskContainer.getItemInfo(), accessibilityInfo); } @Override diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java index 67128f01fe..f4944a642a 100644 --- a/quickstep/src/com/android/quickstep/views/TaskView.java +++ b/quickstep/src/com/android/quickstep/views/TaskView.java @@ -30,6 +30,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; +import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT; import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_UNDEFINED; import static java.lang.annotation.RetentionPolicy.SOURCE; @@ -38,6 +39,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.annotation.IdRes; import android.app.ActivityOptions; import android.content.Context; import android.content.Intent; @@ -1300,10 +1302,14 @@ public class TaskView extends FrameLayout implements Reusable { getContext().getText(R.string.accessibility_close))); final Context context = getContext(); - // TODO(b/200609838) Determine which task to run A11y action on when in split screen - for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, - mActivity.getDeviceProfile(), mTaskIdAttributeContainer[0])) { - info.addAction(s.createAccessibilityAction(context)); + for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) { + if (taskContainer == null) { + continue; + } + for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, + mActivity.getDeviceProfile(), taskContainer)) { + info.addAction(s.createAccessibilityAction(context)); + } } if (mDigitalWellBeingToast.hasLimit()) { @@ -1334,12 +1340,16 @@ public class TaskView extends FrameLayout implements Reusable { return true; } - // TODO(b/200609838) Determine which task to run A11y action on when in split screen - for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, - mActivity.getDeviceProfile(), mTaskIdAttributeContainer[0])) { - if (s.hasHandlerForAction(action)) { - s.onClick(this); - return true; + for (TaskIdAttributeContainer taskContainer : mTaskIdAttributeContainer) { + if (taskContainer == null) { + continue; + } + for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this, + mActivity.getDeviceProfile(), taskContainer)) { + if (s.hasHandlerForAction(action)) { + s.onClick(this); + return true; + } } } @@ -1556,7 +1566,6 @@ public class TaskView extends FrameLayout implements Reusable { mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight); } } - } public class TaskIdAttributeContainer { @@ -1564,12 +1573,16 @@ public class TaskView extends FrameLayout implements Reusable { private final Task mTask; /** Defaults to STAGE_POSITION_UNDEFINED if in not a split screen task view */ private @SplitConfigurationOptions.StagePosition int mStagePosition; + @IdRes + private final int mA11yNodeId; public TaskIdAttributeContainer(Task task, TaskThumbnailView thumbnailView, int stagePosition) { this.mTask = task; this.mThumbnailView = thumbnailView; this.mStagePosition = stagePosition; + this.mA11yNodeId = (stagePosition == STAGE_POSITION_BOTTOM_OR_RIGHT) ? + R.id.split_bottomRight_appInfo : R.id.split_topLeft_appInfo; } public TaskThumbnailView getThumbnailView() { @@ -1595,5 +1608,9 @@ public class TaskView extends FrameLayout implements Reusable { void setStagePosition(@SplitConfigurationOptions.StagePosition int stagePosition) { this.mStagePosition = stagePosition; } + + public int getA11yNodeId() { + return mA11yNodeId; + } } } diff --git a/res/values/id.xml b/res/values/id.xml index ebc4075214..508caffd48 100644 --- a/res/values/id.xml +++ b/res/values/id.xml @@ -21,6 +21,10 @@ + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 5f53d4e24a..868b5f39b8 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -40,9 +40,10 @@ Split screen - Split top - Split left - Split right + Split top + Split left + Split right + App info for %1$s diff --git a/src/com/android/launcher3/popup/SystemShortcut.java b/src/com/android/launcher3/popup/SystemShortcut.java index 826c79b289..af872750a0 100644 --- a/src/com/android/launcher3/popup/SystemShortcut.java +++ b/src/com/android/launcher3/popup/SystemShortcut.java @@ -41,8 +41,8 @@ public abstract class SystemShortcut extend implements View.OnClickListener { private final int mIconResId; - private final int mLabelResId; - private final int mAccessibilityActionId; + protected final int mLabelResId; + protected int mAccessibilityActionId; protected final T mTarget; protected final ItemInfo mItemInfo; @@ -139,11 +139,43 @@ public abstract class SystemShortcut extend public static class AppInfo extends SystemShortcut { + @Nullable + private SplitAccessibilityInfo mSplitA11yInfo; + public AppInfo(T target, ItemInfo itemInfo) { super(R.drawable.ic_info_no_shadow, R.string.app_info_drop_target_label, target, itemInfo); } + /** + * Constructor used by overview for staged split to provide custom A11y information. + * + * Future improvements considerations: + * Have the logic in {@link #createAccessibilityAction(Context)} be moved to super + * call in {@link SystemShortcut#createAccessibilityAction(Context)} by having + * SystemShortcut be aware of TaskContainers and staged split. + * That way it could directly create the correct node info for any shortcut that supports + * split, but then we'll need custom resIDs for each pair of shortcuts. + */ + public AppInfo(T target, ItemInfo itemInfo, SplitAccessibilityInfo accessibilityInfo) { + this(target, itemInfo); + mSplitA11yInfo = accessibilityInfo; + mAccessibilityActionId = accessibilityInfo.nodeId; + } + + @Override + public AccessibilityNodeInfo.AccessibilityAction createAccessibilityAction( + Context context) { + if (mSplitA11yInfo != null && mSplitA11yInfo.containsMultipleTasks) { + String accessibilityLabel = context.getString(R.string.split_app_info_accessibility, + mSplitA11yInfo.taskTitle); + return new AccessibilityNodeInfo.AccessibilityAction(mAccessibilityActionId, + accessibilityLabel); + } else { + return super.createAccessibilityAction(context); + } + } + @Override public void onClick(View view) { dismissTaskMenuView(mTarget); @@ -153,6 +185,19 @@ public abstract class SystemShortcut extend mTarget.getStatsLogManager().logger().withItemInfo(mItemInfo) .log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP); } + + public static class SplitAccessibilityInfo { + public final boolean containsMultipleTasks; + public final CharSequence taskTitle; + public final int nodeId; + + public SplitAccessibilityInfo(boolean containsMultipleTasks, + CharSequence taskTitle, int nodeId) { + this.containsMultipleTasks = containsMultipleTasks; + this.taskTitle = taskTitle; + this.nodeId = nodeId; + } + } } public static final Factory INSTALL = (activity, itemInfo) -> {