From cfe3e454ac37131d2893101cb854fa607c56f09a Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Wed, 15 Apr 2020 15:26:56 +0800 Subject: [PATCH 1/3] Tutorial improvement for Accessibility shortcut (1/n). Because can't save the value into setting keys for the service before version R, and in order to let tutorial dialog can get the consistent value, using alternative that each fragment with shortcut to must implement the new abstract method. Bug: 148989018 Test: manual test Change-Id: Iba2a7daa70eb00f0bba37317c9355531fbb77628 --- ...AccessibilityActivityPreferenceFragment.java | 6 ++++++ ...yAccessibilityServicePreferenceFragment.java | 17 +++++++++++++++++ ...eAccessibilityServicePreferenceFragment.java | 8 +++++++- .../ToggleColorInversionPreferenceFragment.java | 6 ++++++ .../ToggleDaltonizerPreferenceFragment.java | 6 ++++++ .../ToggleFeaturePreferenceFragment.java | 5 +++++ ...leScreenMagnificationPreferenceFragment.java | 5 +++++ .../ToggleFeaturePreferenceFragmentTest.java | 5 +++++ .../ToggleFeaturePreferenceFragmentTest.java | 5 +++++ 9 files changed, 62 insertions(+), 1 deletion(-) 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..6fdbe0310e0 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(); @@ -197,6 +197,12 @@ public class ToggleAccessibilityServicePreferenceFragment extends } } + @Override + int getUserShortcutTypes() { + return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), + mComponentName); + } + @Override protected void updateToggleServiceTitle(SwitchPreference switchPreference) { final AccessibilityServiceInfo info = getAccessibilityServiceInfo(); 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..7fc4623e332 100644 --- a/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java @@ -334,6 +334,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); } diff --git a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java index 91b8eaebd47..11f25b4dd43 100644 --- a/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleScreenMagnificationPreferenceFragment.java @@ -412,6 +412,11 @@ public class ToggleScreenMagnificationPreferenceFragment extends } } + @Override + int getUserShortcutTypes() { + return getUserShortcutTypeFromSettings(getPrefContext()); + } + @Override protected void onPreferenceToggled(String preferenceKey, boolean enabled) { if (enabled && TextUtils.equals( 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 From 72aa60ae9593fa0356bfdbddbd9f4376bff29801 Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Fri, 6 Mar 2020 22:54:00 +0800 Subject: [PATCH 2/3] Tutorial improvement for Accessibility shortcut (2/n). Update UI widgets to meet the new design. Bug: 148989018 Test: manual test Change-Id: Ife42995af193db6746135d29f6fa1ad452d265a6 --- res/drawable/ic_accessibility_new.xml | 4 +- .../ic_accessibility_page_indicator.xml | 26 ++ ...accessibility_shortcut_tutorial_dialog.xml | 58 +++ ...ccessibilityGestureNavigationTutorial.java | 329 +++++++++++++++++- ...ccessibilityServicePreferenceFragment.java | 14 +- .../ToggleFeaturePreferenceFragment.java | 13 +- ...ScreenMagnificationPreferenceFragment.java | 17 +- 7 files changed, 435 insertions(+), 26 deletions(-) create mode 100644 res/drawable/ic_accessibility_page_indicator.xml create mode 100644 res/layout/accessibility_shortcut_tutorial_dialog.xml 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..c3320ad1565 100644 --- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java +++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java @@ -16,35 +16,54 @@ 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.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 +78,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 +112,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 +229,301 @@ 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); + } + + private 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/ToggleAccessibilityServicePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java index 6fdbe0310e0..86181e9fe3b 100644 --- a/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java +++ b/src/com/android/settings/accessibility/ToggleAccessibilityServicePreferenceFragment.java @@ -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); } @@ -307,6 +297,7 @@ public class ToggleAccessibilityServicePreferenceFragment extends } else { AccessibilityUtil.optInAllValuesToSettings(getPrefContext(), shortcutTypes, mComponentName); + showPopupDialog(DialogEnums.LAUNCH_ACCESSIBILITY_TUTORIAL); } } else { AccessibilityUtil.optOutAllValuesFromSettings(getPrefContext(), shortcutTypes, @@ -420,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/ToggleFeaturePreferenceFragment.java b/src/com/android/settings/accessibility/ToggleFeaturePreferenceFragment.java index 7fc4623e332..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; } @@ -663,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 11f25b4dd43..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,7 +406,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends case DialogEnums.MAGNIFICATION_EDIT_SHORTCUT: return SettingsEnums.DIALOG_MAGNIFICATION_EDIT_SHORTCUT; default: - return 0; + return super.getDialogMetricsCategory(dialogId); } } @@ -422,9 +420,7 @@ public class ToggleScreenMagnificationPreferenceFragment extends 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); } @@ -454,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); } From 8cbb44d6f6267d67801ba3874048b5b5f1f87e3a Mon Sep 17 00:00:00 2001 From: Peter_Liang Date: Sat, 14 Mar 2020 17:24:43 +0800 Subject: [PATCH 3/3] Tutorial improvement for Accessibility shortcut (3/n). Add test cases for tutorial pages. Bug: 148989018 Bug: 134640159 Test: manual test & run RoboTests Change-Id: I9bb29f6e61e331c3f9a2408a1eebc3d5e9e16000 --- ...ccessibilityGestureNavigationTutorial.java | 4 +- ...sibilityGestureNavigationTutorialTest.java | 89 +++++++++++++++++++ 2 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 tests/robotests/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorialTest.java diff --git a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java index c3320ad1565..58245b70912 100644 --- a/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java +++ b/src/com/android/settings/accessibility/AccessibilityGestureNavigationTutorial.java @@ -45,6 +45,7 @@ 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; @@ -364,7 +365,8 @@ public final class AccessibilityGestureNavigationTutorial { return new TutorialPage(title, image, indicatorIcon, instruction); } - private static List createShortcutTutorialPages(@NonNull Context context, + @VisibleForTesting + static List createShortcutTutorialPages(@NonNull Context context, int shortcutTypes) { final List tutorialPages = new ArrayList<>(); if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) { 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(); + } +}