diff --git a/res/drawable/ic_accessibility_new.xml b/res/drawable/ic_accessibility_new.xml index 13472397895..265259cf587 100644 --- a/res/drawable/ic_accessibility_new.xml +++ b/res/drawable/ic_accessibility_new.xml @@ -15,8 +15,8 @@ --> diff --git a/res/drawable/ic_accessibility_page_indicator.xml b/res/drawable/ic_accessibility_page_indicator.xml new file mode 100644 index 00000000000..4fa8204c663 --- /dev/null +++ b/res/drawable/ic_accessibility_page_indicator.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/res/layout/accessibility_shortcut_tutorial_dialog.xml b/res/layout/accessibility_shortcut_tutorial_dialog.xml new file mode 100644 index 00000000000..9f3ee432178 --- /dev/null +++ b/res/layout/accessibility_shortcut_tutorial_dialog.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java index ddf7bd442f8..58245b70912 100644 --- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java +++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java @@ -16,35 +16,55 @@ package com.android.settings.accessibility; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; + +import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; + import android.content.Context; import android.content.DialogInterface; import android.content.res.TypedArray; +import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.Spannable; import android.text.SpannableString; import android.text.style.ImageSpan; +import android.util.TypedValue; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.TextureView; import android.view.View; +import android.view.ViewGroup; import android.view.Window; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextSwitcher; import android.widget.TextView; +import androidx.annotation.AnimRes; import androidx.annotation.ColorInt; import androidx.annotation.IntDef; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; +import androidx.core.util.Preconditions; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; import com.android.settings.R; +import com.android.settings.Utils; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; /** * Utility class for creating the dialog that guides users for gesture navigation for * accessibility services. */ -public class AccessibilityGestureNavigationTutorial { - +public final class AccessibilityGestureNavigationTutorial { /** IntDef enum for dialog type. */ @Retention(RetentionPolicy.SOURCE) @IntDef({ @@ -59,6 +79,8 @@ public class AccessibilityGestureNavigationTutorial { int GESTURE_NAVIGATION_SETTINGS = 2; } + private AccessibilityGestureNavigationTutorial() {} + private static final DialogInterface.OnClickListener mOnClickListener = (DialogInterface dialog, int which) -> dialog.dismiss(); @@ -91,6 +113,13 @@ public class AccessibilityGestureNavigationTutorial { return createDialog(context, DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION); } + static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) { + return new AlertDialog.Builder(context) + .setView(createShortcutNavigationContentView(context, shortcutTypes)) + .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener) + .create(); + } + /** * Get a content View for a dialog to confirm that they want to enable a service. * @@ -201,4 +230,302 @@ public class AccessibilityGestureNavigationTutorial { typedArray.recycle(); return colorResId; } + + private static class TutorialPagerAdapter extends PagerAdapter { + private final List mTutorialPages; + private TutorialPagerAdapter(List tutorialPages) { + this.mTutorialPages = tutorialPages; + } + + @NonNull + @Override + public Object instantiateItem(@NonNull ViewGroup container, int position) { + final View itemView = mTutorialPages.get(position).getImageView(); + container.addView(itemView); + return itemView; + } + + @Override + public int getCount() { + return mTutorialPages.size(); + } + + @Override + public boolean isViewFromObject(@NonNull View view, @NonNull Object o) { + return view == o; + } + + @Override + public void destroyItem(@NonNull ViewGroup container, int position, + @NonNull Object object) { + final View itemView = mTutorialPages.get(position).getImageView(); + container.removeView(itemView); + } + } + + private static ImageView createImageView(Context context, int imageRes) { + final ImageView imageView = new ImageView(context); + imageView.setImageResource(imageRes); + imageView.setAdjustViewBounds(true); + + return imageView; + } + + private static View createShortcutNavigationContentView(Context context, int shortcutTypes) { + final LayoutInflater inflater = context.getSystemService(LayoutInflater.class); + final View contentView = inflater.inflate( + R.layout.accessibility_shortcut_tutorial_dialog, /* root= */ null); + final List tutorialPages = + createShortcutTutorialPages(context, shortcutTypes); + Preconditions.checkArgument(!tutorialPages.isEmpty(), + /* errorMessage= */ "Unexpected tutorial pages size"); + + final LinearLayout indicatorContainer = contentView.findViewById(R.id.indicator_container); + indicatorContainer.setVisibility(tutorialPages.size() > 1 ? VISIBLE : GONE); + for (TutorialPage page : tutorialPages) { + indicatorContainer.addView(page.getIndicatorIcon()); + } + tutorialPages.get(/* firstIndex */ 0).getIndicatorIcon().setEnabled(true); + + final TextSwitcher title = contentView.findViewById(R.id.title); + title.setFactory(() -> makeTitleView(context)); + title.setText(tutorialPages.get(/* firstIndex */ 0).getTitle()); + + final TextSwitcher instruction = contentView.findViewById(R.id.instruction); + instruction.setFactory(() -> makeInstructionView(context)); + instruction.setText(tutorialPages.get(/* firstIndex */ 0).getInstruction()); + + final ViewPager viewPager = contentView.findViewById(R.id.view_pager); + viewPager.setAdapter(new TutorialPagerAdapter(tutorialPages)); + viewPager.addOnPageChangeListener( + new TutorialPageChangeListener(context, title, instruction, tutorialPages)); + + return contentView; + } + + private static View makeTitleView(Context context) { + final String familyName = + context.getString( + com.android.internal.R.string.config_headlineFontFamilyMedium); + final TextView textView = new TextView(context); + + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 20); + textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary)); + textView.setGravity(Gravity.CENTER); + textView.setTypeface(Typeface.create(familyName, Typeface.NORMAL)); + + return textView; + } + + private static View makeInstructionView(Context context) { + final TextView textView = new TextView(context); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 16); + textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary)); + textView.setTypeface( + Typeface.create(/* familyName= */ "sans-serif", Typeface.NORMAL)); + return textView; + } + + private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) { + final CharSequence title = getSoftwareTitle(context); + final ImageView image = createSoftwareImage(context); + final CharSequence instruction = getSoftwareInstruction(context); + final ImageView indicatorIcon = + createImageView(context, R.drawable.ic_accessibility_page_indicator); + indicatorIcon.setEnabled(false); + + return new TutorialPage(title, image, indicatorIcon, instruction); + } + + private static TutorialPage createHardwareTutorialPage(@NonNull Context context) { + final CharSequence title = + context.getText(R.string.accessibility_tutorial_dialog_title_volume); + final ImageView image = + createImageView(context, R.drawable.accessibility_shortcut_type_hardware); + final ImageView indicatorIcon = + createImageView(context, R.drawable.ic_accessibility_page_indicator); + final CharSequence instruction = + context.getText(R.string.accessibility_tutorial_dialog_message_volume); + indicatorIcon.setEnabled(false); + + return new TutorialPage(title, image, indicatorIcon, instruction); + } + + private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) { + final CharSequence title = + context.getText(R.string.accessibility_tutorial_dialog_title_triple); + final ImageView image = + createImageView(context, R.drawable.accessibility_shortcut_type_triple_tap); + final CharSequence instruction = + context.getText(R.string.accessibility_tutorial_dialog_message_triple); + final ImageView indicatorIcon = + createImageView(context, R.drawable.ic_accessibility_page_indicator); + indicatorIcon.setEnabled(false); + + return new TutorialPage(title, image, indicatorIcon, instruction); + } + + @VisibleForTesting + static List createShortcutTutorialPages(@NonNull Context context, + int shortcutTypes) { + final List tutorialPages = new ArrayList<>(); + if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { + tutorialPages.add(createSoftwareTutorialPage(context)); + } + + if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) { + tutorialPages.add(createHardwareTutorialPage(context)); + } + + if ((shortcutTypes & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) { + tutorialPages.add(createTripleTapTutorialPage(context)); + } + + return tutorialPages; + } + + private static CharSequence getSoftwareTitle(Context context) { + final boolean isGestureNavigationEnabled = + AccessibilityUtil.isGestureNavigateEnabled(context); + final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context); + + return (isGestureNavigationEnabled || isTouchExploreEnabled) + ? context.getText(R.string.accessibility_tutorial_dialog_title_gesture) + : context.getText(R.string.accessibility_tutorial_dialog_title_button); + } + + private static ImageView createSoftwareImage(Context context) { + int resId = R.drawable.accessibility_shortcut_type_software; + if (AccessibilityUtil.isGestureNavigateEnabled(context)) { + resId = AccessibilityUtil.isTouchExploreEnabled(context) + ? R.drawable.accessibility_shortcut_type_software_gesture_talkback + : R.drawable.accessibility_shortcut_type_software_gesture; + } + + return createImageView(context, resId); + } + + private static CharSequence getSoftwareInstruction(Context context) { + final boolean isGestureNavigateEnabled = + AccessibilityUtil.isGestureNavigateEnabled(context); + final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context); + int resId = R.string.accessibility_tutorial_dialog_message_button; + if (isGestureNavigateEnabled) { + resId = isTouchExploreEnabled + ? R.string.accessibility_tutorial_dialog_message_gesture_talkback + : R.string.accessibility_tutorial_dialog_message_gesture; + } + + CharSequence text = context.getText(resId); + if (resId == R.string.accessibility_tutorial_dialog_message_button) { + text = getSoftwareInstructionWithIcon(context, text); + } + + return text; + } + + private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) { + final String message = text.toString(); + final SpannableString spannableInstruction = SpannableString.valueOf(message); + final int indexIconStart = message.indexOf("%s"); + final int indexIconEnd = indexIconStart + 2; + final ImageView iconView = new ImageView(context); + iconView.setImageDrawable(context.getDrawable(R.drawable.ic_accessibility_new)); + final Drawable icon = iconView.getDrawable().mutate(); + final ImageSpan imageSpan = new ImageSpan(icon); + imageSpan.setContentDescription(""); + icon.setBounds(/* left= */ 0, /* top= */ 0, + icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); + spannableInstruction.setSpan(imageSpan, indexIconStart, indexIconEnd, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + + return spannableInstruction; + } + + private static class TutorialPage { + private final CharSequence mTitle; + private final ImageView mImageView; + private final ImageView mIndicatorIcon; + private final CharSequence mInstruction; + + TutorialPage(CharSequence title, ImageView imageView, ImageView indicatorIcon, + CharSequence instruction) { + this.mTitle = title; + this.mImageView = imageView; + this.mIndicatorIcon = indicatorIcon; + this.mInstruction = instruction; + } + + public CharSequence getTitle() { + return mTitle; + } + + public ImageView getImageView() { + return mImageView; + } + + public ImageView getIndicatorIcon() { + return mIndicatorIcon; + } + + public CharSequence getInstruction() { + return mInstruction; + } + } + + private static class TutorialPageChangeListener implements ViewPager.OnPageChangeListener { + private int mLastTutorialPagePosition = 0; + private final Context mContext; + private final TextSwitcher mTitle; + private final TextSwitcher mInstruction; + private final List mTutorialPages; + + TutorialPageChangeListener(Context context, ViewGroup title, ViewGroup instruction, + List tutorialPages) { + this.mContext = context; + this.mTitle = (TextSwitcher) title; + this.mInstruction = (TextSwitcher) instruction; + this.mTutorialPages = tutorialPages; + } + + @Override + public void onPageScrolled(int position, float positionOffset, + int positionOffsetPixels) { + // Do nothing. + } + + @Override + public void onPageSelected(int position) { + final boolean isPreviousPosition = + mLastTutorialPagePosition > position; + @AnimRes + final int inAnimationResId = isPreviousPosition + ? android.R.anim.slide_in_left + : com.android.internal.R.anim.slide_in_right; + + @AnimRes + final int outAnimationResId = isPreviousPosition + ? android.R.anim.slide_out_right + : com.android.internal.R.anim.slide_out_left; + + mTitle.setInAnimation(mContext, inAnimationResId); + mTitle.setOutAnimation(mContext, outAnimationResId); + mTitle.setText(mTutorialPages.get(position).getTitle()); + + mInstruction.setInAnimation(mContext, inAnimationResId); + mInstruction.setOutAnimation(mContext, outAnimationResId); + mInstruction.setText(mTutorialPages.get(position).getInstruction()); + + for (TutorialPage page : mTutorialPages) { + page.getIndicatorIcon().setEnabled(false); + } + mTutorialPages.get(position).getIndicatorIcon().setEnabled(true); + mLastTutorialPagePosition = position; + } + + @Override + public void onPageScrollStateChanged(int state) { + // Do nothing. + } + } } diff --git a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java index de37babfc32..169a9521323 100644 --- a/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java +++ b/src/com/android/settings/accessibility/LaunchAccessibilityActivityPreferenceFragment.java @@ -100,6 +100,12 @@ public class LaunchAccessibilityActivityPreferenceFragment extends showDialog(DialogEnums.EDIT_SHORTCUT); } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Do not call super. We don't want to see the "Help & feedback" option on this page so as diff --git a/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java index 1803874f09c..4fcfff8e7f7 100644 --- a/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/LegacyAccessibilityServicePreferenceFragment.java @@ -18,6 +18,7 @@ package com.android.settings.accessibility; import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; +import android.accessibilityservice.AccessibilityServiceInfo; import android.os.Bundle; import android.view.View; @@ -48,6 +49,22 @@ public class LegacyAccessibilityServicePreferenceFragment extends setAllowedPreferredShortcutType(UserShortcutType.HARDWARE); } + @Override + int getUserShortcutTypes() { + int shortcutTypes = super.getUserShortcutTypes(); + + final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); + final boolean hasRequestAccessibilityButtonFlag = + (info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0; + if (hasRequestAccessibilityButtonFlag) { + shortcutTypes |= UserShortcutType.SOFTWARE; + } else { + shortcutTypes &= (~UserShortcutType.SOFTWARE); + } + + return shortcutTypes; + } + private void setAllowedPreferredShortcutType(int type) { final AccessibilityUserShortcutType shortcut = new AccessibilityUserShortcutType( mComponentName.flattenToString(), type); diff --git a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index d530a563365..86181e9fe3b 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -106,7 +106,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends // capabilities. For // example, before JellyBean MR2 the user was granting the explore by touch // one. - private AccessibilityServiceInfo getAccessibilityServiceInfo() { + AccessibilityServiceInfo getAccessibilityServiceInfo() { final List infos = AccessibilityManager.getInstance( getPrefContext()).getInstalledAccessibilityServiceList(); @@ -164,16 +164,6 @@ public class ToggleAccessibilityServicePreferenceFragment extends this::onDialogButtonFromDisableToggleClicked); break; } - case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: { - if (AccessibilityUtil.isGestureNavigateEnabled(getPrefContext())) { - mDialog = AccessibilityGestureNavigationTutorial - .showGestureNavigationTutorialDialog(getPrefContext()); - } else { - mDialog = AccessibilityGestureNavigationTutorial - .showAccessibilityButtonTutorialDialog(getPrefContext()); - } - break; - } default: { mDialog = super.onCreateDialog(dialogId); } @@ -197,6 +187,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends } } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + @Override protected void updateToggleServiceTitle(SwitchPreference switchPreference) { final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); @@ -301,6 +297,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends } else { AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } } else { AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, @@ -414,6 +411,9 @@ public class ToggleAccessibilityServicePreferenceFragment extends final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE); AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + mIsDialogShown.set(false); + showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); + mDialog.dismiss(); mShortcutPreference.setSummary(getShortcutTypeSummary(getPrefContext())); diff --git a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java index 07ff7203013..5cde5e602e7 100644 --- a/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleColorInversionPreferenceFragment.java @@ -124,6 +124,12 @@ public class ToggleColorInversionPreferenceFragment extends ToggleFeaturePrefere showDialog(DialogEnums.EDIT_SHORTCUT); } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + private void updateSwitchBarToggleSwitch() { final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON; if (mToggleServiceDividerSwitchPreference.isChecked() == checked) { diff --git a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java index 5b9c55b3a93..c7212a44b21 100644 --- a/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleDaltonizerPreferenceFragment.java @@ -198,6 +198,12 @@ public final class ToggleDaltonizerPreferenceFragment extends ToggleFeaturePrefe showDialog(DialogEnums.EDIT_SHORTCUT); } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + private void updateSwitchBarToggleSwitch() { final boolean checked = Settings.Secure.getInt(getContentResolver(), ENABLED, OFF) == ON; if (mToggleServiceDividerSwitchPreference.isChecked() == checked) { diff --git a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index eeb1f3ae5cf..8b4f3f25bd7 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -42,7 +42,6 @@ import android.view.accessibility.AccessibilityManager.TouchExplorationStateChan import android.widget.CheckBox; import android.widget.ImageView; -import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; import androidx.preference.PreferenceCategory; import androidx.preference.PreferenceScreen; @@ -250,14 +249,21 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference @Override public Dialog onCreateDialog(int dialogId) { + Dialog dialog; switch (dialogId) { case DialogEnums.EDIT_SHORTCUT: final CharSequence dialogTitle = getPrefContext().getString( R.string.accessibility_shortcut_title, mPackageName); - final AlertDialog dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( + dialog = AccessibilityEditDialogUtils.showEditShortcutDialog( getPrefContext(), dialogTitle, this::callOnAlertDialogCheckboxClicked); initializeDialogCheckBox(dialog); return dialog; + case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: + dialog = AccessibilityGestureNavigationTutorial + .createAccessibilityTutorialDialog(getPrefContext(), + getUserShortcutTypes()); + dialog.setCanceledOnTouchOutside(false); + return dialog; default: throw new IllegalArgumentException("Unsupported dialogId " + dialogId); } @@ -268,6 +274,8 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference switch (dialogId) { case DialogEnums.EDIT_SHORTCUT: return SettingsEnums.DIALOG_ACCESSIBILITY_SERVICE_EDIT_SHORTCUT; + case DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL: + return SettingsEnums.DIALOG_ACCESSIBILITY_TUTORIAL; default: return SettingsEnums.ACTION_UNKNOWN; } @@ -334,6 +342,11 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference removeActionBarToggleSwitch(); } + /** + * Returns the shortcut type list which has been checked by user. + */ + abstract int getUserShortcutTypes(); + protected void updateToggleServiceTitle(SwitchPreference switchPreference) { switchPreference.setTitle(R.string.accessibility_service_master_switch_title); } @@ -658,6 +671,7 @@ public abstract class ToggleFeaturePreferenceFragment extends SettingsPreference if (preference.isChecked()) { AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } else { AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, mComponentName); diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index 91b8eaebd47..b550434fc8e 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -224,24 +224,22 @@ public class ToggleScreenMagnificationPreferenceFragment extends @Override public Dialog onCreateDialog(int dialogId) { + final AlertDialog dialog; switch (dialogId) { case DialogEnums.GESTURE_NAVIGATION_TUTORIAL: return AccessibilityGestureNavigationTutorial .showGestureNavigationTutorialDialog(getPrefContext()); - case DialogEnums.ACCESSIBILITY_BUTTON_TUTORIAL: - return AccessibilityGestureNavigationTutorial - .showAccessibilityButtonTutorialDialog(getPrefContext()); case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT: final CharSequence dialogTitle = getPrefContext().getString( R.string.accessibility_shortcut_title, mPackageName); - final AlertDialog dialog = - AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog( + dialog = AccessibilityEditDialogUtils.showMagnificationEditShortcutDialog( getPrefContext(), dialogTitle, this::callOnAlertDialogCheckboxClicked); initializeDialogCheckBox(dialog); return dialog; + default: + return super.onCreateDialog(dialogId); } - throw new IllegalArgumentException("Unsupported dialogId " + dialogId); } private void setDialogTextAreaClickListener(View dialogView, CheckBox checkBox) { @@ -408,18 +406,21 @@ public class ToggleScreenMagnificationPreferenceFragment extends case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT: return SettingsEnums.DIALOG_MAGNIFICATION_EDIT_SHORTCUT; default: - return 0; + return super.getDialogMetricsCategory(dialogId); } } + @Override + int getUserShortcutTypes() { + return getUserShortcutTypeFromSettings(getPrefContext()); + } + @Override protected void onPreferenceToggled(String preferenceKey, boolean enabled) { if (enabled && TextUtils.equals( Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, preferenceKey)) { - showDialog(AccessibilityUtil.isGestureNavigateEnabled(getPrefContext()) - ? DialogEnums.GESTURE_NAVIGATION_TUTORIAL - : DialogEnums.ACCESSIBILITY_BUTTON_TUTORIAL); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } MagnificationPreferenceFragment.setChecked(getContentResolver(), preferenceKey, enabled); } @@ -449,6 +450,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends final int shortcutTypes = getUserShortcutTypes(getPrefContext(), UserShortcutType.SOFTWARE); if (preference.isChecked()) { optInAllMagnificationValuesToSettings(getPrefContext(), shortcutTypes); + showDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } else { optOutAllMagnificationValuesFromSettings(getPrefContext(), shortcutTypes); } diff --git a/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java b/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java new file mode 100644 index 00000000000..cf0ce96bf60 --- /dev/null +++ b/tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.accessibility; + +import static com.android.settings.accessibility.AccessibilityGestureNavigationTutorial.createAccessibilityTutorialDialog; +import static com.android.settings.accessibility.AccessibilityGestureNavigationTutorial.createShortcutTutorialPages; +import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType; + +import static com.google.common.truth.Truth.assertThat; + +import android.content.Context; + +import androidx.appcompat.app.AlertDialog; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +/** Tests for {@link AccessibilityGestureNavigationTutorial}. */ +@RunWith(RobolectricTestRunner.class) +public final class AccessibilityGestureNavigationTutorialTest { + + private Context mContext; + private int mShortcutTypes; + + @Before + public void setUp() { + mContext = RuntimeEnvironment.application; + mShortcutTypes = /* initial */ 0; + } + + @Test(expected = IllegalArgumentException.class) + public void createTutorialPages_shortcutListIsEmpty_throwsException() { + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + } + + @Test + public void createTutorialPages_turnOnTripleTapShortcut_hasOnePage() { + mShortcutTypes |= UserShortcutType.TRIPLETAP; + + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + + assertThat(createShortcutTutorialPages(mContext, + mShortcutTypes)).hasSize(/* expectedSize= */ 1); + assertThat(alertDialog).isNotNull(); + } + + @Test + public void createTutorialPages_turnOnSoftwareShortcut_hasOnePage() { + mShortcutTypes |= UserShortcutType.SOFTWARE; + + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + + assertThat(createShortcutTutorialPages(mContext, + mShortcutTypes)).hasSize(/* expectedSize= */ 1); + assertThat(alertDialog).isNotNull(); + } + + @Test + public void createTutorialPages_turnOnSoftwareAndHardwareShortcuts_hasTwoPages() { + mShortcutTypes |= UserShortcutType.SOFTWARE; + mShortcutTypes |= UserShortcutType.HARDWARE; + + final AlertDialog alertDialog = + createAccessibilityTutorialDialog(mContext, mShortcutTypes); + + assertThat(createShortcutTutorialPages(mContext, + mShortcutTypes)).hasSize(/* expectedSize= */ 2); + assertThat(alertDialog).isNotNull(); + } +} diff --git a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index e476c21f37d..3df9c048637 100644 --- a/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/robotests/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -129,6 +129,11 @@ public class ToggleFeaturePreferenceFragmentTest { return 0; } + @Override + int getUserShortcutTypes() { + return 0; + } + @Override public int getPreferenceScreenResId() { return R.xml.placeholder_prefs; diff --git a/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java b/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java index 9a3b526dd7a..e0013e08f8c 100644 --- a/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java +++ b/tests/unit/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragmentTest.java @@ -86,5 +86,10 @@ public class ToggleFeaturePreferenceFragmentTest { public int getMetricsCategory() { return 0; } + + @Override + int getUserShortcutTypes() { + return 0; + } } } \ No newline at end of file