Fix talkback not automatically announcing in Gesture Nav Tutorial
This change explicitly sets the Talkback attributes to allow for
automatically scrolling to the subtitle in the gesture navigation
tutorial. Previously, you would have to tap the subtitle for it to be
announced.
Fix: 386884587
Test: Run the tutorial with talkback enabled and observe talkback announcing the
title and subtitle of the current gesture
Flag: EXEMPT bugfix
Change-Id: I6ca2c1654f9e481165e2135e9afd72fa178f8184
diff --git a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
index b004dfd..7530c28 100644
--- a/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
+++ b/quickstep/res/layout/redesigned_gesture_tutorial_fragment.xml
@@ -147,6 +147,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="104dp"
android:accessibilityHeading="true"
+ android:accessibilityTraversalBefore="@id/gesture_tutorial_fragment_feedback_subtitle"
android:gravity="top"
android:lineSpacingExtra="-1sp"
android:textAppearance="@style/TextAppearance.GestureTutorial.MainTitle"
@@ -161,6 +162,8 @@
android:layout_marginTop="24dp"
android:lineSpacingExtra="4sp"
android:textAppearance="@style/TextAppearance.GestureTutorial.MainSubtitle"
+ android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_feedback_title"
+ android:accessibilityTraversalBefore="@id/gesture_tutorial_fragment_action_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/gesture_tutorial_fragment_feedback_title" />
@@ -224,6 +227,10 @@
android:layout_marginBottom="@dimen/gesture_tutorial_done_button_bottom_margin"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
+ android:clickable="true"
+ android:focusableInTouchMode="true"
+ android:accessibilityTraversalAfter="@id/gesture_tutorial_fragment_feedback_subtitle"
+ android:contentDescription="@string/gesture_tutorial_action_button_label"
android:background="@drawable/gesture_tutorial_action_button_background"
android:stateListAnimator="@null"
android:text="@string/gesture_tutorial_action_button_label"
diff --git a/quickstep/src/com/android/quickstep/interaction/TutorialController.java b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
index 7d14a3e..0fc95e2 100644
--- a/quickstep/src/com/android/quickstep/interaction/TutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/TutorialController.java
@@ -80,8 +80,8 @@
private static final CharSequence DEFAULT_PIXEL_TIPS_APP_NAME = "Pixel Tips";
private static final int FEEDBACK_ANIMATION_MS = 133;
- private static final int RIPPLE_VISIBLE_MS = 300;
- private static final int GESTURE_ANIMATION_DELAY_MS = 1500;
+ private static final int SUBTITLE_ANNOUNCE_DELAY_MS = 3000;
+ private static final int DONE_BUTTON_ANNOUNCE_DELAY_MS = 4000;
private static final int ADVANCE_TUTORIAL_TIMEOUT_MS = 3000;
private static final long GESTURE_ANIMATION_PAUSE_DURATION_MILLIS = 1000;
protected float mExitingAppEndingCornerRadius;
@@ -124,10 +124,12 @@
// These runnables should be used when posting callbacks to their views and cleared from their
// views before posting new callbacks.
private final Runnable mTitleViewCallback;
+ private final Runnable mSubtitleViewCallback;
@Nullable private Runnable mFeedbackViewCallback;
@Nullable private Runnable mFakeTaskViewCallback;
@Nullable private Runnable mFakeTaskbarViewCallback;
private final Runnable mShowFeedbackRunnable;
+ private final AccessibilityManager mAccessibilityManager;
TutorialController(TutorialFragment tutorialFragment, TutorialType tutorialType) {
mTutorialFragment = tutorialFragment;
@@ -175,6 +177,7 @@
mFeedbackTitleView.setText(getIntroductionTitle());
mFeedbackSubtitleView.setText(getIntroductionSubtitle());
+
mExitingAppView.setClipToOutline(true);
mExitingAppView.setOutlineProvider(new ViewOutlineProvider() {
@Override
@@ -183,8 +186,16 @@
}
});
- mTitleViewCallback = () -> mFeedbackTitleView.sendAccessibilityEvent(
- AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ mAccessibilityManager = AccessibilityManager.getInstance(mContext);
+ mTitleViewCallback = () -> {
+ mFeedbackTitleView.requestFocus();
+ mFeedbackTitleView.sendAccessibilityEvent(
+ AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ };
+ mSubtitleViewCallback = () -> {
+ mFeedbackSubtitleView.requestFocus();
+ mFeedbackSubtitleView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ };
mShowFeedbackRunnable = () -> {
mFeedbackView.setAlpha(0f);
mFeedbackView.setScaleX(0.95f);
@@ -203,10 +214,10 @@
mFeedbackViewCallback = mTutorialFragment::continueTutorial;
mFeedbackView.postDelayed(
mFeedbackViewCallback,
- AccessibilityManager.getInstance(mContext)
- .getRecommendedTimeoutMillis(
- ADVANCE_TUTORIAL_TIMEOUT_MS,
- AccessibilityManager.FLAG_CONTENT_TEXT));
+ mAccessibilityManager.getRecommendedTimeoutMillis(
+ ADVANCE_TUTORIAL_TIMEOUT_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT
+ | AccessibilityManager.FLAG_CONTENT_CONTROLS));
}
})
.start();
@@ -404,6 +415,7 @@
int subtitleResId,
boolean isGestureSuccessful) {
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+ mFeedbackSubtitleView.removeCallbacks(mSubtitleViewCallback);
if (mFeedbackViewCallback != null) {
mFeedbackView.removeCallbacks(mFeedbackViewCallback);
mFeedbackViewCallback = null;
@@ -411,6 +423,15 @@
mFeedbackTitleView.setText(titleResId);
mFeedbackSubtitleView.setText(subtitleResId);
+ mFeedbackTitleView.postDelayed(mTitleViewCallback, mAccessibilityManager
+ .getRecommendedTimeoutMillis(
+ FEEDBACK_ANIMATION_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT));
+ mFeedbackSubtitleView.postDelayed(mSubtitleViewCallback, mAccessibilityManager
+ .getRecommendedTimeoutMillis(
+ SUBTITLE_ANNOUNCE_DELAY_MS,
+ AccessibilityManager.FLAG_CONTENT_TEXT));
+
if (isGestureSuccessful) {
if (mTutorialFragment.isAtFinalStep()) {
showActionButton();
@@ -467,6 +488,7 @@
mFakeTaskbarViewCallback = null;
}
mFeedbackTitleView.removeCallbacks(mTitleViewCallback);
+ mFeedbackSubtitleView.removeCallbacks(mSubtitleViewCallback);
}
private void playFeedbackAnimation() {
@@ -542,6 +564,13 @@
mSkipButton.setVisibility(GONE);
mDoneButton.setVisibility(View.VISIBLE);
mDoneButton.setOnClickListener(this::onActionButtonClicked);
+ mDoneButton.postDelayed(() -> {
+ mDoneButton.requestFocus();
+ mDoneButton.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
+ }, mAccessibilityManager
+ .getRecommendedTimeoutMillis(
+ DONE_BUTTON_ANNOUNCE_DELAY_MS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS));
}
void hideFakeTaskbar(boolean animateToHotseat) {