Add a button to the accessibility button tutorial dialog that links directly to the settings page.
When the accessibility button appears, some users don't know how to control it. Add a button to the tutorial dialog that links directly to the settings page to help users get more information. Bug: 183977141 Test: make RunSettingsRoboTests ROBOTEST_FILTER=AccessibilityGestureNavigationTutorialTest Change-Id: I08d7e5b0771b6bf99f7753ccbcf2e7400227ddc5
This commit is contained in:
@@ -5395,6 +5395,8 @@
|
|||||||
<string name="accessibility_tutorial_dialog_message_gesture_settings_talkback">To use an accessibility feature, swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold.</string>
|
<string name="accessibility_tutorial_dialog_message_gesture_settings_talkback">To use an accessibility feature, swipe up from the bottom of the screen with 3 fingers.\n\nTo switch between features, swipe up with 3 fingers and hold.</string>
|
||||||
<!-- Button for the accessibility tutorial dialog to dismiss the dialog when user clicks it. [CHAR LIMIT=10] -->
|
<!-- Button for the accessibility tutorial dialog to dismiss the dialog when user clicks it. [CHAR LIMIT=10] -->
|
||||||
<string name="accessibility_tutorial_dialog_button">Got it</string>
|
<string name="accessibility_tutorial_dialog_button">Got it</string>
|
||||||
|
<!-- Button for the accessibility tutorial dialog to link to accessibility settings page. [CHAR LIMIT=30] -->
|
||||||
|
<string name="accessibility_tutorial_dialog_link_button">Accessibility button settings</string>
|
||||||
<!-- Title for accessibility shortcut preference for accessibility apps. [CHAR LIMIT=40] -->
|
<!-- Title for accessibility shortcut preference for accessibility apps. [CHAR LIMIT=40] -->
|
||||||
<string name="accessibility_shortcut_title"><xliff:g id="service" example="Select to Speak">%1$s</xliff:g> shortcut</string>
|
<string name="accessibility_shortcut_title"><xliff:g id="service" example="Select to Speak">%1$s</xliff:g> shortcut</string>
|
||||||
<!-- Title for software shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
|
<!-- Title for software shortcut in accessibility edit shortcut dialog. [CHAR LIMIT=NONE] -->
|
||||||
|
@@ -21,6 +21,7 @@ import static android.view.View.VISIBLE;
|
|||||||
|
|
||||||
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||||
|
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
@@ -57,6 +58,7 @@ import androidx.viewpager.widget.PagerAdapter;
|
|||||||
import androidx.viewpager.widget.ViewPager;
|
import androidx.viewpager.widget.ViewPager;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
|
|
||||||
import com.airbnb.lottie.LottieAnimationView;
|
import com.airbnb.lottie.LottieAnimationView;
|
||||||
import com.airbnb.lottie.LottieDrawable;
|
import com.airbnb.lottie.LottieDrawable;
|
||||||
@@ -130,12 +132,29 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes,
|
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes,
|
||||||
@Nullable DialogInterface.OnClickListener negativeButtonListener) {
|
@Nullable DialogInterface.OnClickListener actionButtonListener) {
|
||||||
return new AlertDialog.Builder(context)
|
|
||||||
.setView(createShortcutNavigationContentView(context, shortcutTypes))
|
final int category = SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS;
|
||||||
.setNegativeButton(R.string.accessibility_tutorial_dialog_button,
|
final DialogInterface.OnClickListener linkButtonListener =
|
||||||
negativeButtonListener)
|
(dialog, which) -> new SubSettingLauncher(context)
|
||||||
|
.setDestination(AccessibilityButtonFragment.class.getName())
|
||||||
|
.setSourceMetricsCategory(category)
|
||||||
|
.launch();
|
||||||
|
|
||||||
|
final AlertDialog alertDialog = new AlertDialog.Builder(context)
|
||||||
|
.setNegativeButton(R.string.accessibility_tutorial_dialog_link_button,
|
||||||
|
linkButtonListener)
|
||||||
|
.setPositiveButton(R.string.accessibility_tutorial_dialog_button,
|
||||||
|
actionButtonListener)
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
|
final TutorialPageChangeListener.OnPageSelectedCallback callback =
|
||||||
|
type -> alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).setVisibility(
|
||||||
|
type == UserShortcutType.SOFTWARE ? VISIBLE : GONE);
|
||||||
|
|
||||||
|
alertDialog.setView(createShortcutNavigationContentView(context, shortcutTypes, callback));
|
||||||
|
|
||||||
|
return alertDialog;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -311,7 +330,9 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
return inflater.inflate(R.layout.accessibility_lottie_animation_view, /* root= */ null);
|
return inflater.inflate(R.layout.accessibility_lottie_animation_view, /* root= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static View createShortcutNavigationContentView(Context context, int shortcutTypes) {
|
private static View createShortcutNavigationContentView(Context context, int shortcutTypes,
|
||||||
|
TutorialPageChangeListener.OnPageSelectedCallback onPageSelectedCallback) {
|
||||||
|
|
||||||
final LayoutInflater inflater = context.getSystemService(LayoutInflater.class);
|
final LayoutInflater inflater = context.getSystemService(LayoutInflater.class);
|
||||||
final View contentView = inflater.inflate(
|
final View contentView = inflater.inflate(
|
||||||
R.layout.accessibility_shortcut_tutorial_dialog, /* root= */ null);
|
R.layout.accessibility_shortcut_tutorial_dialog, /* root= */ null);
|
||||||
@@ -342,9 +363,10 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
viewPager.setImportantForAccessibility(tutorialPages.size() > 1
|
viewPager.setImportantForAccessibility(tutorialPages.size() > 1
|
||||||
? View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
? View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||||
: View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
: View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||||
viewPager.addOnPageChangeListener(
|
|
||||||
new TutorialPageChangeListener(context, viewPager, title, instruction,
|
TutorialPageChangeListener listener = new TutorialPageChangeListener(context, viewPager,
|
||||||
tutorialPages));
|
title, instruction, tutorialPages);
|
||||||
|
listener.setOnPageSelectedCallback(onPageSelectedCallback);
|
||||||
|
|
||||||
return contentView;
|
return contentView;
|
||||||
}
|
}
|
||||||
@@ -365,6 +387,7 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
|
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
|
||||||
|
final int type = UserShortcutType.SOFTWARE;
|
||||||
final CharSequence title = getSoftwareTitle(context);
|
final CharSequence title = getSoftwareTitle(context);
|
||||||
final View image = createSoftwareImage(context);
|
final View image = createSoftwareImage(context);
|
||||||
final CharSequence instruction = getSoftwareInstruction(context);
|
final CharSequence instruction = getSoftwareInstruction(context);
|
||||||
@@ -372,10 +395,11 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
createImageView(context, R.drawable.ic_accessibility_page_indicator);
|
createImageView(context, R.drawable.ic_accessibility_page_indicator);
|
||||||
indicatorIcon.setEnabled(false);
|
indicatorIcon.setEnabled(false);
|
||||||
|
|
||||||
return new TutorialPage(title, image, indicatorIcon, instruction);
|
return new TutorialPage(type, title, image, indicatorIcon, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TutorialPage createHardwareTutorialPage(@NonNull Context context) {
|
private static TutorialPage createHardwareTutorialPage(@NonNull Context context) {
|
||||||
|
final int type = UserShortcutType.HARDWARE;
|
||||||
final CharSequence title =
|
final CharSequence title =
|
||||||
context.getText(R.string.accessibility_tutorial_dialog_title_volume);
|
context.getText(R.string.accessibility_tutorial_dialog_title_volume);
|
||||||
final View image =
|
final View image =
|
||||||
@@ -386,10 +410,11 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
context.getText(R.string.accessibility_tutorial_dialog_message_volume);
|
context.getText(R.string.accessibility_tutorial_dialog_message_volume);
|
||||||
indicatorIcon.setEnabled(false);
|
indicatorIcon.setEnabled(false);
|
||||||
|
|
||||||
return new TutorialPage(title, image, indicatorIcon, instruction);
|
return new TutorialPage(type, title, image, indicatorIcon, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) {
|
private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) {
|
||||||
|
final int type = UserShortcutType.TRIPLETAP;
|
||||||
final CharSequence title =
|
final CharSequence title =
|
||||||
context.getText(R.string.accessibility_tutorial_dialog_title_triple);
|
context.getText(R.string.accessibility_tutorial_dialog_title_triple);
|
||||||
final View image =
|
final View image =
|
||||||
@@ -401,7 +426,7 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
createImageView(context, R.drawable.ic_accessibility_page_indicator);
|
createImageView(context, R.drawable.ic_accessibility_page_indicator);
|
||||||
indicatorIcon.setEnabled(false);
|
indicatorIcon.setEnabled(false);
|
||||||
|
|
||||||
return new TutorialPage(title, image, indicatorIcon, instruction);
|
return new TutorialPage(type, title, image, indicatorIcon, instruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -485,13 +510,15 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static class TutorialPage {
|
private static class TutorialPage {
|
||||||
|
private final int mType;
|
||||||
private final CharSequence mTitle;
|
private final CharSequence mTitle;
|
||||||
private final View mIllustrationView;
|
private final View mIllustrationView;
|
||||||
private final ImageView mIndicatorIcon;
|
private final ImageView mIndicatorIcon;
|
||||||
private final CharSequence mInstruction;
|
private final CharSequence mInstruction;
|
||||||
|
|
||||||
TutorialPage(CharSequence title, View illustrationView, ImageView indicatorIcon,
|
TutorialPage(int type, CharSequence title, View illustrationView, ImageView indicatorIcon,
|
||||||
CharSequence instruction) {
|
CharSequence instruction) {
|
||||||
|
this.mType = type;
|
||||||
this.mTitle = title;
|
this.mTitle = title;
|
||||||
this.mIllustrationView = illustrationView;
|
this.mIllustrationView = illustrationView;
|
||||||
this.mIndicatorIcon = indicatorIcon;
|
this.mIndicatorIcon = indicatorIcon;
|
||||||
@@ -500,6 +527,10 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
setupIllustrationChildViewsGravity();
|
setupIllustrationChildViewsGravity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getType() {
|
||||||
|
return mType;
|
||||||
|
}
|
||||||
|
|
||||||
public CharSequence getTitle() {
|
public CharSequence getTitle() {
|
||||||
return mTitle;
|
return mTitle;
|
||||||
}
|
}
|
||||||
@@ -541,6 +572,7 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
private final TextSwitcher mInstruction;
|
private final TextSwitcher mInstruction;
|
||||||
private final List<TutorialPage> mTutorialPages;
|
private final List<TutorialPage> mTutorialPages;
|
||||||
private final ViewPager mViewPager;
|
private final ViewPager mViewPager;
|
||||||
|
private OnPageSelectedCallback mOnPageSelectedCallback;
|
||||||
|
|
||||||
TutorialPageChangeListener(Context context, ViewPager viewPager, ViewGroup title,
|
TutorialPageChangeListener(Context context, ViewPager viewPager, ViewGroup title,
|
||||||
ViewGroup instruction, List<TutorialPage> tutorialPages) {
|
ViewGroup instruction, List<TutorialPage> tutorialPages) {
|
||||||
@@ -549,6 +581,14 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
this.mTitle = (TextSwitcher) title;
|
this.mTitle = (TextSwitcher) title;
|
||||||
this.mInstruction = (TextSwitcher) instruction;
|
this.mInstruction = (TextSwitcher) instruction;
|
||||||
this.mTutorialPages = tutorialPages;
|
this.mTutorialPages = tutorialPages;
|
||||||
|
this.mOnPageSelectedCallback = null;
|
||||||
|
|
||||||
|
this.mViewPager.addOnPageChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnPageSelectedCallback(
|
||||||
|
OnPageSelectedCallback callback) {
|
||||||
|
this.mOnPageSelectedCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -589,11 +629,22 @@ public final class AccessibilityGestureNavigationTutorial {
|
|||||||
mViewPager.setContentDescription(
|
mViewPager.setContentDescription(
|
||||||
mContext.getString(R.string.accessibility_tutorial_pager,
|
mContext.getString(R.string.accessibility_tutorial_pager,
|
||||||
currentPageNumber, mTutorialPages.size()));
|
currentPageNumber, mTutorialPages.size()));
|
||||||
|
|
||||||
|
if (mOnPageSelectedCallback != null) {
|
||||||
|
mOnPageSelectedCallback.onPageSelected(mTutorialPages.get(position).getType());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPageScrollStateChanged(int state) {
|
public void onPageScrollStateChanged(int state) {
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** The interface that provides a callback method after tutorial page is selected. */
|
||||||
|
private interface OnPageSelectedCallback {
|
||||||
|
|
||||||
|
/** The callback method after tutorial page is selected. */
|
||||||
|
void onPageSelected(int type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,14 +23,21 @@ import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutT
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.robolectric.Shadows.shadowOf;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.settings.SettingsEnums;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
import androidx.appcompat.app.AlertDialog;
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.SettingsActivity;
|
||||||
|
import com.android.settings.SubSettings;
|
||||||
|
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@@ -39,6 +46,7 @@ import org.junit.runner.RunWith;
|
|||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.junit.MockitoJUnit;
|
import org.mockito.junit.MockitoJUnit;
|
||||||
import org.mockito.junit.MockitoRule;
|
import org.mockito.junit.MockitoRule;
|
||||||
|
import org.robolectric.Robolectric;
|
||||||
import org.robolectric.RobolectricTestRunner;
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
/** Tests for {@link AccessibilityGestureNavigationTutorial}. */
|
/** Tests for {@link AccessibilityGestureNavigationTutorial}. */
|
||||||
@@ -103,26 +111,44 @@ public final class AccessibilityGestureNavigationTutorialTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void performClickOnNegativeButton_turnOnSoftwareShortcut_dismiss() {
|
public void performClickOnPositiveButton_turnOnSoftwareShortcut_dismiss() {
|
||||||
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
||||||
final AlertDialog alertDialog =
|
final AlertDialog alertDialog =
|
||||||
createAccessibilityTutorialDialog(mContext, mShortcutTypes);
|
createAccessibilityTutorialDialog(mContext, mShortcutTypes);
|
||||||
alertDialog.show();
|
alertDialog.show();
|
||||||
|
|
||||||
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
|
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
|
||||||
|
|
||||||
assertThat(alertDialog.isShowing()).isFalse();
|
assertThat(alertDialog.isShowing()).isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void performClickOnNegativeButton_turnOnSoftwareShortcut_callOnClickListener() {
|
public void performClickOnPositiveButton_turnOnSoftwareShortcut_callOnClickListener() {
|
||||||
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
||||||
final AlertDialog alertDialog =
|
final AlertDialog alertDialog =
|
||||||
createAccessibilityTutorialDialog(mContext, mShortcutTypes, mMockOnClickListener);
|
createAccessibilityTutorialDialog(mContext, mShortcutTypes, mMockOnClickListener);
|
||||||
alertDialog.show();
|
alertDialog.show();
|
||||||
|
|
||||||
|
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
|
||||||
|
|
||||||
|
verify(mMockOnClickListener).onClick(alertDialog, DialogInterface.BUTTON_POSITIVE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void performClickOnNegativeButton_turnOnSoftwareShortcut_directToSettingsPage() {
|
||||||
|
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
||||||
|
Activity activity = Robolectric.buildActivity(Activity.class).create().get();
|
||||||
|
final AlertDialog alertDialog =
|
||||||
|
createAccessibilityTutorialDialog(activity, mShortcutTypes, mMockOnClickListener);
|
||||||
|
alertDialog.show();
|
||||||
|
|
||||||
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
|
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
|
||||||
|
|
||||||
verify(mMockOnClickListener).onClick(alertDialog, DialogInterface.BUTTON_NEGATIVE);
|
final Intent intent = shadowOf(activity).peekNextStartedActivity();
|
||||||
|
assertThat(intent.getComponent().getClassName()).isEqualTo(SubSettings.class.getName());
|
||||||
|
assertThat(intent.getStringExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT))
|
||||||
|
.isEqualTo(AccessibilityButtonFragment.class.getName());
|
||||||
|
assertThat(intent.getIntExtra(MetricsFeatureProvider.EXTRA_SOURCE_METRICS_CATEGORY, -1))
|
||||||
|
.isEqualTo(SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user