Merge "Improve KQS accessibility" into main

This commit is contained in:
Treehugger Robot
2025-03-10 12:49:00 -07:00
committed by Android (Google) Code Review
4 changed files with 73 additions and 14 deletions
@@ -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"
+8
View File
@@ -325,6 +325,12 @@
<!-- Label for creating an application bubble (from the Taskbar only). -->
<string name="open_app_as_a_bubble">Open app as a bubble</string>
<!-- Accessibility pane title for quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
<string name="quick_switch_pane_title">Recent apps</string>
<!-- Content description for the quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
<string name="quick_switch_content_description">Recent app list</string>
<!-- Label for quick switch tile showing how many more apps are available. The number will be displayed above this text. [CHAR LIMIT=NONE] -->
<string name="quick_switch_overflow">{count, plural,
=1{more app}
@@ -336,6 +342,8 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
<!-- Accessibility label for quick switch tiles that include information about the tile's position in the parent list [CHAR LIMIT=NONE] -->
<string name="quick_switch_task_with_position_in_parent"><xliff:g id="task_description" example="Chrome">%1$s</xliff:g>, item <xliff:g id="index_in_parent" example="1">%2$d</xliff:g> of <xliff:g id="total_tasks" example="5">%3$d</xliff:g></string>
<!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left
TODO(b/397975686): Make these translatable when verified by UX. -->
@@ -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<ThumbnailData> callback);
@@ -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();
}
}
});