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();
+ }
}
});