Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 57d13ad0 authored by Angela Wang's avatar Angela Wang
Browse files

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
parent 1468ba54
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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] -->
+64 −13
Original line number Diff line number Diff line
@@ -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);
        }
    }
}
+30 −4
Original line number Diff line number Diff line
@@ -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);
    }
}