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>
|
||||
<!-- 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>
|
||||
<!-- 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] -->
|
||||
<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] -->
|
||||
|
@@ -21,6 +21,7 @@ import static android.view.View.VISIBLE;
|
||||
|
||||
import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
|
||||
|
||||
import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -57,6 +58,7 @@ import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.core.SubSettingLauncher;
|
||||
|
||||
import com.airbnb.lottie.LottieAnimationView;
|
||||
import com.airbnb.lottie.LottieDrawable;
|
||||
@@ -130,12 +132,29 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
}
|
||||
|
||||
static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes,
|
||||
@Nullable DialogInterface.OnClickListener negativeButtonListener) {
|
||||
return new AlertDialog.Builder(context)
|
||||
.setView(createShortcutNavigationContentView(context, shortcutTypes))
|
||||
.setNegativeButton(R.string.accessibility_tutorial_dialog_button,
|
||||
negativeButtonListener)
|
||||
@Nullable DialogInterface.OnClickListener actionButtonListener) {
|
||||
|
||||
final int category = SettingsEnums.SWITCH_SHORTCUT_DIALOG_ACCESSIBILITY_BUTTON_SETTINGS;
|
||||
final DialogInterface.OnClickListener linkButtonListener =
|
||||
(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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 View contentView = inflater.inflate(
|
||||
R.layout.accessibility_shortcut_tutorial_dialog, /* root= */ null);
|
||||
@@ -342,9 +363,10 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
viewPager.setImportantForAccessibility(tutorialPages.size() > 1
|
||||
? View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
: View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
|
||||
viewPager.addOnPageChangeListener(
|
||||
new TutorialPageChangeListener(context, viewPager, title, instruction,
|
||||
tutorialPages));
|
||||
|
||||
TutorialPageChangeListener listener = new TutorialPageChangeListener(context, viewPager,
|
||||
title, instruction, tutorialPages);
|
||||
listener.setOnPageSelectedCallback(onPageSelectedCallback);
|
||||
|
||||
return contentView;
|
||||
}
|
||||
@@ -365,6 +387,7 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
}
|
||||
|
||||
private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
|
||||
final int type = UserShortcutType.SOFTWARE;
|
||||
final CharSequence title = getSoftwareTitle(context);
|
||||
final View image = createSoftwareImage(context);
|
||||
final CharSequence instruction = getSoftwareInstruction(context);
|
||||
@@ -372,10 +395,11 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
createImageView(context, R.drawable.ic_accessibility_page_indicator);
|
||||
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) {
|
||||
final int type = UserShortcutType.HARDWARE;
|
||||
final CharSequence title =
|
||||
context.getText(R.string.accessibility_tutorial_dialog_title_volume);
|
||||
final View image =
|
||||
@@ -386,10 +410,11 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
context.getText(R.string.accessibility_tutorial_dialog_message_volume);
|
||||
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) {
|
||||
final int type = UserShortcutType.TRIPLETAP;
|
||||
final CharSequence title =
|
||||
context.getText(R.string.accessibility_tutorial_dialog_title_triple);
|
||||
final View image =
|
||||
@@ -401,7 +426,7 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
createImageView(context, R.drawable.ic_accessibility_page_indicator);
|
||||
indicatorIcon.setEnabled(false);
|
||||
|
||||
return new TutorialPage(title, image, indicatorIcon, instruction);
|
||||
return new TutorialPage(type, title, image, indicatorIcon, instruction);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -485,13 +510,15 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
}
|
||||
|
||||
private static class TutorialPage {
|
||||
private final int mType;
|
||||
private final CharSequence mTitle;
|
||||
private final View mIllustrationView;
|
||||
private final ImageView mIndicatorIcon;
|
||||
private final CharSequence mInstruction;
|
||||
|
||||
TutorialPage(CharSequence title, View illustrationView, ImageView indicatorIcon,
|
||||
TutorialPage(int type, CharSequence title, View illustrationView, ImageView indicatorIcon,
|
||||
CharSequence instruction) {
|
||||
this.mType = type;
|
||||
this.mTitle = title;
|
||||
this.mIllustrationView = illustrationView;
|
||||
this.mIndicatorIcon = indicatorIcon;
|
||||
@@ -500,6 +527,10 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
setupIllustrationChildViewsGravity();
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
@@ -541,6 +572,7 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
private final TextSwitcher mInstruction;
|
||||
private final List<TutorialPage> mTutorialPages;
|
||||
private final ViewPager mViewPager;
|
||||
private OnPageSelectedCallback mOnPageSelectedCallback;
|
||||
|
||||
TutorialPageChangeListener(Context context, ViewPager viewPager, ViewGroup title,
|
||||
ViewGroup instruction, List<TutorialPage> tutorialPages) {
|
||||
@@ -549,6 +581,14 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
this.mTitle = (TextSwitcher) title;
|
||||
this.mInstruction = (TextSwitcher) instruction;
|
||||
this.mTutorialPages = tutorialPages;
|
||||
this.mOnPageSelectedCallback = null;
|
||||
|
||||
this.mViewPager.addOnPageChangeListener(this);
|
||||
}
|
||||
|
||||
public void setOnPageSelectedCallback(
|
||||
OnPageSelectedCallback callback) {
|
||||
this.mOnPageSelectedCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -589,11 +629,22 @@ public final class AccessibilityGestureNavigationTutorial {
|
||||
mViewPager.setContentDescription(
|
||||
mContext.getString(R.string.accessibility_tutorial_pager,
|
||||
currentPageNumber, mTutorialPages.size()));
|
||||
|
||||
if (mOnPageSelectedCallback != null) {
|
||||
mOnPageSelectedCallback.onPageSelected(mTutorialPages.get(position).getType());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
// 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 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.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
|
||||
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.Rule;
|
||||
@@ -39,6 +46,7 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnit;
|
||||
import org.mockito.junit.MockitoRule;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
|
||||
/** Tests for {@link AccessibilityGestureNavigationTutorial}. */
|
||||
@@ -103,26 +111,44 @@ public final class AccessibilityGestureNavigationTutorialTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performClickOnNegativeButton_turnOnSoftwareShortcut_dismiss() {
|
||||
public void performClickOnPositiveButton_turnOnSoftwareShortcut_dismiss() {
|
||||
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
||||
final AlertDialog alertDialog =
|
||||
createAccessibilityTutorialDialog(mContext, mShortcutTypes);
|
||||
alertDialog.show();
|
||||
|
||||
alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE).performClick();
|
||||
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
|
||||
|
||||
assertThat(alertDialog.isShowing()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performClickOnNegativeButton_turnOnSoftwareShortcut_callOnClickListener() {
|
||||
public void performClickOnPositiveButton_turnOnSoftwareShortcut_callOnClickListener() {
|
||||
mShortcutTypes |= UserShortcutType.SOFTWARE;
|
||||
final AlertDialog alertDialog =
|
||||
createAccessibilityTutorialDialog(mContext, mShortcutTypes, mMockOnClickListener);
|
||||
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();
|
||||
|
||||
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