diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml index 885bdb9f3b..2dea79c8a2 100644 --- a/quickstep/res/layout/keyboard_quick_switch_view.xml +++ b/quickstep/res/layout/keyboard_quick_switch_view.xml @@ -18,6 +18,8 @@ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/keyboard_quick_switch_view" + android:contentDescription="@string/quick_switch_content_description" + android:accessibilityPaneTitle="@string/quick_switch_pane_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top" diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml index 65f4b3cd2e..7578bd546f 100644 --- a/quickstep/res/values/strings.xml +++ b/quickstep/res/values/strings.xml @@ -325,6 +325,12 @@ Open app as a bubble + + Recent apps + + + Recent app list + {count, plural, =1{more app} @@ -336,6 +342,8 @@ %1$s and %2$s + + %1$s, item %2$d of %3$d diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java index 5f867cd080..15be03a55b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java @@ -66,6 +66,11 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout { @Nullable private ImageView mIcon2; @Nullable private View mContent; + // Describe the task position in the parent container. Used to add information about the task's + // position in a task list to the task view's content description. + private int mIndexInParent = -1; + private int mTotalTasksInParent = -1; + public KeyboardQuickSwitchTaskView(@NonNull Context context) { this(context, null); } @@ -155,36 +160,51 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout { applyThumbnail(mThumbnailView1, task1, thumbnailUpdateFunction); applyThumbnail(mThumbnailView2, task2, thumbnailUpdateFunction); + // Update content description, even in cases task icons, and content descriptions need to be + // loaded asynchronously to ensure that the task has non empty description (assuming task + // position information was set), as KeyboardQuickSwitch view may request accessibility + // focus to be moved to the task when the quick switch UI gets shown. The description will + // be updated once the task metadata has been loaded - the delay should be very short, and + // the content description when task titles are not available still gives some useful + // information to the user (the task's position in the list). + updateContentDesctiptionForTasks(task1, task2); + if (iconUpdateFunction == null) { applyIcon(mIcon1, task1); applyIcon(mIcon2, task2); - setContentDescription(task2 == null - ? task1.titleDescription - : getContext().getString( - R.string.quick_switch_split_task, - task1.titleDescription, - task2.titleDescription)); return; } + iconUpdateFunction.updateIconInBackground(task1, t -> { applyIcon(mIcon1, task1); if (task2 != null) { return; } - setContentDescription(task1.titleDescription); + updateContentDesctiptionForTasks(task1, null); }); + if (task2 == null) { return; } iconUpdateFunction.updateIconInBackground(task2, t -> { applyIcon(mIcon2, task2); - setContentDescription(getContext().getString( - R.string.quick_switch_split_task, - task1.titleDescription, - task2.titleDescription)); + updateContentDesctiptionForTasks(task1, task2); }); } + /** + * Initializes information about the task's position within the parent container context - used + * to add position information to the view's content description. + * Should be called before associating the view with tasks. + * + * @param index The view's 0-based index within the parent task container. + * @param totalTasks The total number of tasks in the parent task container. + */ + protected void setPositionInformation(int index, int totalTasks) { + mIndexInParent = index; + mTotalTasksInParent = totalTasks; + } + protected void setThumbnailsForSplitTasks( @NonNull Task task1, @Nullable Task task2, @@ -283,6 +303,28 @@ public class KeyboardQuickSwitchTaskView extends ConstraintLayout { constantState.newDrawable(getResources(), getContext().getTheme())); } + /** + * Updates the task view's content description to reflect tasks represented by the view. + */ + private void updateContentDesctiptionForTasks(@NonNull Task task1, @Nullable Task task2) { + String tasksDescription = task1.titleDescription == null || task2 == null + ? task1.titleDescription + : getContext().getString( + R.string.quick_switch_split_task, + task1.titleDescription, + task2.titleDescription); + if (mIndexInParent < 0) { + setContentDescription(tasksDescription); + return; + } + + setContentDescription( + getContext().getString(R.string.quick_switch_task_with_position_in_parent, + tasksDescription != null ? tasksDescription : "", + mIndexInParent + 1, + mTotalTasksInParent)); + } + protected interface ThumbnailUpdateFunction { void updateThumbnailInBackground(Task task, Consumer callback); diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java index 845e19f0c2..ab147bbcd9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java +++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java @@ -301,6 +301,7 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { continue; } + currentTaskView.setPositionInformation(i, tasksToDisplay); currentTaskView.setThumbnailsForSplitTasks( task1, task2, @@ -548,6 +549,9 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { ViewOutlineProvider outlineProvider = getOutlineProvider(); + int defaultFocusedTaskIndex = Math.min( + getTaskCount() - 1, + currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride); mOpenAnimation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { @@ -601,9 +605,7 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { }); } - animateFocusMove(-1, Math.min( - getTaskCount() - 1, - currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride)); + animateFocusMove(-1, defaultFocusedTaskIndex); displayedContent.setVisibility(VISIBLE); setVisibility(VISIBLE); requestFocus(); @@ -623,6 +625,11 @@ public class KeyboardQuickSwitchView extends ConstraintLayout { invalidateOutline(); mOpenAnimation = null; InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN); + + View focusedTask = getTaskAt(defaultFocusedTaskIndex); + if (focusedTask != null) { + focusedTask.requestAccessibilityFocus(); + } } });