1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.settings.accessibility;
18 
19 import static android.view.View.GONE;
20 import static android.view.View.VISIBLE;
21 
22 import static com.android.settings.accessibility.AccessibilityUtil.UserShortcutType;
23 
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.res.TypedArray;
27 import android.graphics.Typeface;
28 import android.graphics.drawable.Drawable;
29 import android.text.Spannable;
30 import android.text.SpannableString;
31 import android.text.style.ImageSpan;
32 import android.util.TypedValue;
33 import android.view.Gravity;
34 import android.view.LayoutInflater;
35 import android.view.TextureView;
36 import android.view.View;
37 import android.view.ViewGroup;
38 import android.view.Window;
39 import android.widget.ImageView;
40 import android.widget.LinearLayout;
41 import android.widget.TextSwitcher;
42 import android.widget.TextView;
43 
44 import androidx.annotation.AnimRes;
45 import androidx.annotation.ColorInt;
46 import androidx.annotation.IntDef;
47 import androidx.annotation.NonNull;
48 import androidx.annotation.VisibleForTesting;
49 import androidx.appcompat.app.AlertDialog;
50 import androidx.core.content.ContextCompat;
51 import androidx.core.util.Preconditions;
52 import androidx.viewpager.widget.PagerAdapter;
53 import androidx.viewpager.widget.ViewPager;
54 
55 import com.android.settings.R;
56 import com.android.settings.Utils;
57 
58 import java.lang.annotation.Retention;
59 import java.lang.annotation.RetentionPolicy;
60 import java.util.ArrayList;
61 import java.util.List;
62 
63 /**
64  * Utility class for creating the dialog that guides users for gesture navigation for
65  * accessibility services.
66  */
67 public final class AccessibilityGestureNavigationTutorial {
68     /** IntDef enum for dialog type. */
69     @Retention(RetentionPolicy.SOURCE)
70     @IntDef({
71             DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON,
72             DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION,
73             DialogType.GESTURE_NAVIGATION_SETTINGS,
74     })
75 
76     private @interface DialogType {
77         int LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON = 0;
78         int LAUNCH_SERVICE_BY_GESTURE_NAVIGATION = 1;
79         int GESTURE_NAVIGATION_SETTINGS = 2;
80     }
81 
AccessibilityGestureNavigationTutorial()82     private AccessibilityGestureNavigationTutorial() {}
83 
84     private static final DialogInterface.OnClickListener mOnClickListener =
85             (DialogInterface dialog, int which) -> dialog.dismiss();
86 
showGestureNavigationSettingsTutorialDialog(Context context, DialogInterface.OnDismissListener dismissListener)87     public static void showGestureNavigationSettingsTutorialDialog(Context context,
88             DialogInterface.OnDismissListener dismissListener) {
89         final AlertDialog alertDialog = new AlertDialog.Builder(context)
90                 .setView(createTutorialDialogContentView(context,
91                         DialogType.GESTURE_NAVIGATION_SETTINGS))
92                 .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
93                 .setOnDismissListener(dismissListener)
94                 .create();
95 
96         alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
97         alertDialog.setCanceledOnTouchOutside(false);
98         alertDialog.show();
99     }
100 
showAccessibilityButtonTutorialDialog(Context context)101     static AlertDialog showAccessibilityButtonTutorialDialog(Context context) {
102         final AlertDialog alertDialog = createDialog(context,
103                 DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON);
104 
105         if (!AccessibilityUtil.isGestureNavigateEnabled(context)) {
106             updateMessageWithIcon(context, alertDialog);
107         }
108 
109         return alertDialog;
110     }
111 
showGestureNavigationTutorialDialog(Context context)112     static AlertDialog showGestureNavigationTutorialDialog(Context context) {
113         return createDialog(context, DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION);
114     }
115 
createAccessibilityTutorialDialog(Context context, int shortcutTypes)116     static AlertDialog createAccessibilityTutorialDialog(Context context, int shortcutTypes) {
117         return new AlertDialog.Builder(context)
118                 .setView(createShortcutNavigationContentView(context, shortcutTypes))
119                 .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
120                 .create();
121     }
122 
123     /**
124      * Get a content View for a dialog to confirm that they want to enable a service.
125      *
126      * @param context    A valid context
127      * @param dialogType The type of tutorial dialog
128      * @return A content view suitable for viewing
129      */
createTutorialDialogContentView(Context context, int dialogType)130     private static View createTutorialDialogContentView(Context context, int dialogType) {
131         final LayoutInflater inflater = (LayoutInflater) context.getSystemService(
132                 Context.LAYOUT_INFLATER_SERVICE);
133 
134         View content = null;
135 
136         switch (dialogType) {
137             case DialogType.LAUNCH_SERVICE_BY_ACCESSIBILITY_BUTTON:
138                 content = inflater.inflate(
139                         R.layout.tutorial_dialog_launch_service_by_accessibility_button, null);
140                 break;
141             case DialogType.LAUNCH_SERVICE_BY_GESTURE_NAVIGATION:
142                 content = inflater.inflate(
143                         R.layout.tutorial_dialog_launch_service_by_gesture_navigation, null);
144                 final TextureView gestureTutorialVideo = content.findViewById(
145                         R.id.gesture_tutorial_video);
146                 final TextView gestureTutorialMessage = content.findViewById(
147                         R.id.gesture_tutorial_message);
148                 VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
149                                 ? R.raw.illustration_accessibility_gesture_three_finger
150                                 : R.raw.illustration_accessibility_gesture_two_finger,
151                         gestureTutorialVideo);
152                 gestureTutorialMessage.setText(AccessibilityUtil.isTouchExploreEnabled(context)
153                         ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
154                         : R.string.accessibility_tutorial_dialog_message_gesture);
155                 break;
156             case DialogType.GESTURE_NAVIGATION_SETTINGS:
157                 content = inflater.inflate(
158                         R.layout.tutorial_dialog_launch_by_gesture_navigation_settings, null);
159                 final TextureView gestureSettingsTutorialVideo = content.findViewById(
160                         R.id.gesture_tutorial_video);
161                 final TextView gestureSettingsTutorialMessage = content.findViewById(
162                         R.id.gesture_tutorial_message);
163                 VideoPlayer.create(context, AccessibilityUtil.isTouchExploreEnabled(context)
164                                 ? R.raw.illustration_accessibility_gesture_three_finger
165                                 : R.raw.illustration_accessibility_gesture_two_finger,
166                         gestureSettingsTutorialVideo);
167                 final int stringResId = AccessibilityUtil.isTouchExploreEnabled(context)
168                         ? R.string.accessibility_tutorial_dialog_message_gesture_settings_talkback
169                         : R.string.accessibility_tutorial_dialog_message_gesture_settings;
170                 gestureSettingsTutorialMessage.setText(stringResId);
171                 break;
172         }
173 
174         return content;
175     }
176 
createDialog(Context context, int dialogType)177     private static AlertDialog createDialog(Context context, int dialogType) {
178         final AlertDialog alertDialog = new AlertDialog.Builder(context)
179                 .setView(createTutorialDialogContentView(context, dialogType))
180                 .setNegativeButton(R.string.accessibility_tutorial_dialog_button, mOnClickListener)
181                 .create();
182 
183         alertDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
184         alertDialog.setCanceledOnTouchOutside(false);
185         alertDialog.show();
186 
187         return alertDialog;
188     }
189 
updateMessageWithIcon(Context context, AlertDialog alertDialog)190     private static void updateMessageWithIcon(Context context, AlertDialog alertDialog) {
191         final TextView gestureTutorialMessage = alertDialog.findViewById(
192                 R.id.button_tutorial_message);
193 
194         // Get the textView line height to update [icon] size. Must be called after show()
195         final int lineHeight = gestureTutorialMessage.getLineHeight();
196         gestureTutorialMessage.setText(getMessageStringWithIcon(context, lineHeight));
197     }
198 
getMessageStringWithIcon(Context context, int lineHeight)199     private static SpannableString getMessageStringWithIcon(Context context, int lineHeight) {
200         final String messageString = context
201                 .getString(R.string.accessibility_tutorial_dialog_message_button);
202         final SpannableString spannableMessage = SpannableString.valueOf(messageString);
203 
204         // Icon
205         final int indexIconStart = messageString.indexOf("%s");
206         final int indexIconEnd = indexIconStart + 2;
207         final Drawable icon = context.getDrawable(R.drawable.ic_accessibility_new);
208         final ImageSpan imageSpan = new ImageSpan(icon);
209         imageSpan.setContentDescription("");
210         icon.setBounds(0, 0, lineHeight, lineHeight);
211         spannableMessage.setSpan(
212                 imageSpan, indexIconStart, indexIconEnd,
213                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
214 
215         return spannableMessage;
216     }
217 
218     /** Returns the color associated with the specified attribute in the context's theme. */
219     @ColorInt
getThemeAttrColor(final Context context, final int attributeColor)220     private static int getThemeAttrColor(final Context context, final int attributeColor) {
221         final int colorResId = getAttrResourceId(context, attributeColor);
222         return ContextCompat.getColor(context, colorResId);
223     }
224 
225     /** Returns the identifier of the resolved resource assigned to the given attribute. */
getAttrResourceId(final Context context, final int attributeColor)226     private static int getAttrResourceId(final Context context, final int attributeColor) {
227         final int[] attrs = {attributeColor};
228         final TypedArray typedArray = context.obtainStyledAttributes(attrs);
229         final int colorResId = typedArray.getResourceId(0, 0);
230         typedArray.recycle();
231         return colorResId;
232     }
233 
234     private static class TutorialPagerAdapter extends PagerAdapter {
235         private final List<TutorialPage> mTutorialPages;
TutorialPagerAdapter(List<TutorialPage> tutorialPages)236         private TutorialPagerAdapter(List<TutorialPage> tutorialPages) {
237             this.mTutorialPages = tutorialPages;
238         }
239 
240         @NonNull
241         @Override
instantiateItem(@onNull ViewGroup container, int position)242         public Object instantiateItem(@NonNull ViewGroup container, int position) {
243             final View itemView = mTutorialPages.get(position).getImageView();
244             container.addView(itemView);
245             return itemView;
246         }
247 
248         @Override
getCount()249         public int getCount() {
250             return mTutorialPages.size();
251         }
252 
253         @Override
isViewFromObject(@onNull View view, @NonNull Object o)254         public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
255             return view == o;
256         }
257 
258         @Override
destroyItem(@onNull ViewGroup container, int position, @NonNull Object object)259         public void destroyItem(@NonNull ViewGroup container, int position,
260                 @NonNull Object object) {
261             final View itemView = mTutorialPages.get(position).getImageView();
262             container.removeView(itemView);
263         }
264     }
265 
createImageView(Context context, int imageRes)266     private static ImageView createImageView(Context context, int imageRes) {
267         final ImageView imageView = new ImageView(context);
268         imageView.setImageResource(imageRes);
269         imageView.setAdjustViewBounds(true);
270 
271         return imageView;
272     }
273 
createShortcutNavigationContentView(Context context, int shortcutTypes)274     private static View createShortcutNavigationContentView(Context context, int shortcutTypes) {
275         final LayoutInflater inflater = context.getSystemService(LayoutInflater.class);
276         final View contentView = inflater.inflate(
277                 R.layout.accessibility_shortcut_tutorial_dialog, /* root= */ null);
278         final List<TutorialPage> tutorialPages =
279                 createShortcutTutorialPages(context, shortcutTypes);
280         Preconditions.checkArgument(!tutorialPages.isEmpty(),
281                 /* errorMessage= */ "Unexpected tutorial pages size");
282 
283         final LinearLayout indicatorContainer = contentView.findViewById(R.id.indicator_container);
284         indicatorContainer.setVisibility(tutorialPages.size() > 1 ? VISIBLE : GONE);
285         for (TutorialPage page : tutorialPages) {
286             indicatorContainer.addView(page.getIndicatorIcon());
287         }
288         tutorialPages.get(/* firstIndex */ 0).getIndicatorIcon().setEnabled(true);
289 
290         final TextSwitcher title = contentView.findViewById(R.id.title);
291         title.setFactory(() -> makeTitleView(context));
292         title.setText(tutorialPages.get(/* firstIndex */ 0).getTitle());
293 
294         final TextSwitcher instruction = contentView.findViewById(R.id.instruction);
295         instruction.setFactory(() -> makeInstructionView(context));
296         instruction.setText(tutorialPages.get(/* firstIndex */ 0).getInstruction());
297 
298         final ViewPager viewPager = contentView.findViewById(R.id.view_pager);
299         viewPager.setAdapter(new TutorialPagerAdapter(tutorialPages));
300         viewPager.setContentDescription(context.getString(R.string.accessibility_tutorial_pager,
301                 /* firstPage */ 1, tutorialPages.size()));
302         viewPager.setImportantForAccessibility(tutorialPages.size() > 1
303                 ? View.IMPORTANT_FOR_ACCESSIBILITY_YES
304                 : View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
305         viewPager.addOnPageChangeListener(
306                 new TutorialPageChangeListener(context, viewPager, title, instruction,
307                         tutorialPages));
308 
309         return contentView;
310     }
311 
makeTitleView(Context context)312     private static View makeTitleView(Context context) {
313         final String familyName =
314                 context.getString(
315                         com.android.internal.R.string.config_headlineFontFamilyMedium);
316         final TextView textView = new TextView(context);
317 
318         textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 20);
319         textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary));
320         textView.setGravity(Gravity.CENTER);
321         textView.setTypeface(Typeface.create(familyName, Typeface.NORMAL));
322 
323         return textView;
324     }
325 
makeInstructionView(Context context)326     private static View makeInstructionView(Context context) {
327         final TextView textView = new TextView(context);
328         textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, /* size= */ 16);
329         textView.setTextColor(Utils.getColorAttr(context, android.R.attr.textColorPrimary));
330         textView.setTypeface(
331                 Typeface.create(/* familyName= */ "sans-serif", Typeface.NORMAL));
332         return textView;
333     }
334 
createSoftwareTutorialPage(@onNull Context context)335     private static TutorialPage createSoftwareTutorialPage(@NonNull Context context) {
336         final CharSequence title = getSoftwareTitle(context);
337         final ImageView image = createSoftwareImage(context);
338         final CharSequence instruction = getSoftwareInstruction(context);
339         final ImageView indicatorIcon =
340                 createImageView(context, R.drawable.ic_accessibility_page_indicator);
341         indicatorIcon.setEnabled(false);
342 
343         return new TutorialPage(title, image, indicatorIcon, instruction);
344     }
345 
createHardwareTutorialPage(@onNull Context context)346     private static TutorialPage createHardwareTutorialPage(@NonNull Context context) {
347         final CharSequence title =
348                 context.getText(R.string.accessibility_tutorial_dialog_title_volume);
349         final ImageView image =
350                 createImageView(context, R.drawable.accessibility_shortcut_type_hardware);
351         final ImageView indicatorIcon =
352                 createImageView(context, R.drawable.ic_accessibility_page_indicator);
353         final CharSequence instruction =
354                 context.getText(R.string.accessibility_tutorial_dialog_message_volume);
355         indicatorIcon.setEnabled(false);
356 
357         return new TutorialPage(title, image, indicatorIcon, instruction);
358     }
359 
createTripleTapTutorialPage(@onNull Context context)360     private static TutorialPage createTripleTapTutorialPage(@NonNull Context context) {
361         final CharSequence title =
362                 context.getText(R.string.accessibility_tutorial_dialog_title_triple);
363         final ImageView image =
364                 createImageView(context, R.drawable.accessibility_shortcut_type_triple_tap);
365         final CharSequence instruction =
366                 context.getText(R.string.accessibility_tutorial_dialog_message_triple);
367         final ImageView indicatorIcon =
368                 createImageView(context, R.drawable.ic_accessibility_page_indicator);
369         indicatorIcon.setEnabled(false);
370 
371         return new TutorialPage(title, image, indicatorIcon, instruction);
372     }
373 
374     @VisibleForTesting
createShortcutTutorialPages(@onNull Context context, int shortcutTypes)375     static List<TutorialPage> createShortcutTutorialPages(@NonNull Context context,
376             int shortcutTypes) {
377         final List<TutorialPage> tutorialPages = new ArrayList<>();
378         if ((shortcutTypes & UserShortcutType.SOFTWARE) == UserShortcutType.SOFTWARE) {
379             tutorialPages.add(createSoftwareTutorialPage(context));
380         }
381 
382         if ((shortcutTypes & UserShortcutType.HARDWARE) == UserShortcutType.HARDWARE) {
383             tutorialPages.add(createHardwareTutorialPage(context));
384         }
385 
386         if ((shortcutTypes & UserShortcutType.TRIPLETAP) == UserShortcutType.TRIPLETAP) {
387             tutorialPages.add(createTripleTapTutorialPage(context));
388         }
389 
390         return tutorialPages;
391     }
392 
getSoftwareTitle(Context context)393     private static CharSequence getSoftwareTitle(Context context) {
394         final boolean isGestureNavigationEnabled =
395                 AccessibilityUtil.isGestureNavigateEnabled(context);
396         final int resId = isGestureNavigationEnabled
397                 ? R.string.accessibility_tutorial_dialog_title_gesture
398                 : R.string.accessibility_tutorial_dialog_title_button;
399 
400         return context.getText(resId);
401     }
402 
createSoftwareImage(Context context)403     private static ImageView createSoftwareImage(Context context) {
404         int resId = R.drawable.accessibility_shortcut_type_software;
405         if (AccessibilityUtil.isGestureNavigateEnabled(context)) {
406             resId = AccessibilityUtil.isTouchExploreEnabled(context)
407                     ? R.drawable.accessibility_shortcut_type_software_gesture_talkback
408                     : R.drawable.accessibility_shortcut_type_software_gesture;
409         }
410 
411         return createImageView(context, resId);
412     }
413 
getSoftwareInstruction(Context context)414     private static CharSequence getSoftwareInstruction(Context context) {
415         final boolean isGestureNavigateEnabled =
416                 AccessibilityUtil.isGestureNavigateEnabled(context);
417         final boolean isTouchExploreEnabled = AccessibilityUtil.isTouchExploreEnabled(context);
418         int resId = R.string.accessibility_tutorial_dialog_message_button;
419         if (isGestureNavigateEnabled) {
420             resId = isTouchExploreEnabled
421                     ? R.string.accessibility_tutorial_dialog_message_gesture_talkback
422                     : R.string.accessibility_tutorial_dialog_message_gesture;
423         }
424 
425         CharSequence text = context.getText(resId);
426         if (resId == R.string.accessibility_tutorial_dialog_message_button) {
427             text = getSoftwareInstructionWithIcon(context, text);
428         }
429 
430         return text;
431     }
432 
getSoftwareInstructionWithIcon(Context context, CharSequence text)433     private static CharSequence getSoftwareInstructionWithIcon(Context context, CharSequence text) {
434         final String message = text.toString();
435         final SpannableString spannableInstruction = SpannableString.valueOf(message);
436         final int indexIconStart = message.indexOf("%s");
437         final int indexIconEnd = indexIconStart + 2;
438         final ImageView iconView = new ImageView(context);
439         iconView.setImageDrawable(context.getDrawable(R.drawable.ic_accessibility_new));
440         final Drawable icon = iconView.getDrawable().mutate();
441         final ImageSpan imageSpan = new ImageSpan(icon);
442         imageSpan.setContentDescription("");
443         icon.setBounds(/* left= */ 0, /* top= */ 0,
444                 icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
445         spannableInstruction.setSpan(imageSpan, indexIconStart, indexIconEnd,
446                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
447 
448         return spannableInstruction;
449     }
450 
451     private static class TutorialPage {
452         private final CharSequence mTitle;
453         private final ImageView mImageView;
454         private final ImageView mIndicatorIcon;
455         private final CharSequence mInstruction;
456 
TutorialPage(CharSequence title, ImageView imageView, ImageView indicatorIcon, CharSequence instruction)457         TutorialPage(CharSequence title, ImageView imageView, ImageView indicatorIcon,
458                 CharSequence instruction) {
459             this.mTitle = title;
460             this.mImageView = imageView;
461             this.mIndicatorIcon = indicatorIcon;
462             this.mInstruction = instruction;
463         }
464 
getTitle()465         public CharSequence getTitle() {
466             return mTitle;
467         }
468 
getImageView()469         public ImageView getImageView() {
470             return mImageView;
471         }
472 
getIndicatorIcon()473         public ImageView getIndicatorIcon() {
474             return mIndicatorIcon;
475         }
476 
getInstruction()477         public CharSequence getInstruction() {
478             return mInstruction;
479         }
480     }
481 
482     private static class TutorialPageChangeListener implements ViewPager.OnPageChangeListener {
483         private int mLastTutorialPagePosition = 0;
484         private final Context mContext;
485         private final TextSwitcher mTitle;
486         private final TextSwitcher mInstruction;
487         private final List<TutorialPage> mTutorialPages;
488         private final ViewPager mViewPager;
489 
TutorialPageChangeListener(Context context, ViewPager viewPager, ViewGroup title, ViewGroup instruction, List<TutorialPage> tutorialPages)490         TutorialPageChangeListener(Context context, ViewPager viewPager, ViewGroup title,
491                 ViewGroup instruction, List<TutorialPage> tutorialPages) {
492             this.mContext = context;
493             this.mViewPager = viewPager;
494             this.mTitle = (TextSwitcher) title;
495             this.mInstruction = (TextSwitcher) instruction;
496             this.mTutorialPages = tutorialPages;
497         }
498 
499         @Override
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)500         public void onPageScrolled(int position, float positionOffset,
501                 int positionOffsetPixels) {
502             // Do nothing.
503         }
504 
505         @Override
onPageSelected(int position)506         public void onPageSelected(int position) {
507             final boolean isPreviousPosition =
508                     mLastTutorialPagePosition > position;
509             @AnimRes
510             final int inAnimationResId = isPreviousPosition
511                     ? android.R.anim.slide_in_left
512                     : com.android.internal.R.anim.slide_in_right;
513 
514             @AnimRes
515             final int outAnimationResId = isPreviousPosition
516                     ? android.R.anim.slide_out_right
517                     : com.android.internal.R.anim.slide_out_left;
518 
519             mTitle.setInAnimation(mContext, inAnimationResId);
520             mTitle.setOutAnimation(mContext, outAnimationResId);
521             mTitle.setText(mTutorialPages.get(position).getTitle());
522 
523             mInstruction.setInAnimation(mContext, inAnimationResId);
524             mInstruction.setOutAnimation(mContext, outAnimationResId);
525             mInstruction.setText(mTutorialPages.get(position).getInstruction());
526 
527             for (TutorialPage page : mTutorialPages) {
528                 page.getIndicatorIcon().setEnabled(false);
529             }
530             mTutorialPages.get(position).getIndicatorIcon().setEnabled(true);
531             mLastTutorialPagePosition = position;
532 
533             final int currentPageNumber = position + 1;
534             mViewPager.setContentDescription(
535                     mContext.getString(R.string.accessibility_tutorial_pager,
536                             currentPageNumber, mTutorialPages.size()));
537         }
538 
539         @Override
onPageScrollStateChanged(int state)540         public void onPageScrollStateChanged(int state) {
541             // Do nothing.
542         }
543     }
544 }
545