diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 55fa851bbda..77fd94ef5a3 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -943,6 +943,17 @@
+
+
+
+
+
+
+
diff --git a/res/layout-sw600dp/accessibility_tutorial_container.xml b/res/layout-sw600dp/accessibility_tutorial_container.xml
new file mode 100644
index 00000000000..2895a290708
--- /dev/null
+++ b/res/layout-sw600dp/accessibility_tutorial_container.xml
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/accessibility_tutorial_1.xml b/res/layout/accessibility_tutorial_1.xml
new file mode 100644
index 00000000000..710e3276329
--- /dev/null
+++ b/res/layout/accessibility_tutorial_1.xml
@@ -0,0 +1,26 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/accessibility_tutorial_2.xml b/res/layout/accessibility_tutorial_2.xml
new file mode 100644
index 00000000000..5f9c69d648e
--- /dev/null
+++ b/res/layout/accessibility_tutorial_2.xml
@@ -0,0 +1,21 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/accessibility_tutorial_app_icon.xml b/res/layout/accessibility_tutorial_app_icon.xml
new file mode 100644
index 00000000000..fe8399efaa3
--- /dev/null
+++ b/res/layout/accessibility_tutorial_app_icon.xml
@@ -0,0 +1,20 @@
+
+
+
+
\ No newline at end of file
diff --git a/res/layout/accessibility_tutorial_container.xml b/res/layout/accessibility_tutorial_container.xml
new file mode 100644
index 00000000000..e949d2a0fd1
--- /dev/null
+++ b/res/layout/accessibility_tutorial_container.xml
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
index 9bd23360eeb..994d4bb372f 100755
--- a/res/values-sw600dp-land/dimens.xml
+++ b/res/values-sw600dp-land/dimens.xml
@@ -16,4 +16,6 @@
128dip
+ 72dip
+ 48dip
diff --git a/res/values-sw600dp/colors.xml b/res/values-sw600dp/colors.xml
deleted file mode 100644
index 444733374e1..00000000000
--- a/res/values-sw600dp/colors.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-
-
- #20ffffff
-
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index bc9f18dda00..9469a992f67 100755
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -18,4 +18,10 @@
40dip
40dip
40dip
+ 72dip
+ 48dip
+ 48dip
+ 48dip
+ 16dip
+ 26dip
diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml
index 1e99e45bebc..3403f3eab55 100644
--- a/res/values-sw600dp/styles.xml
+++ b/res/values-sw600dp/styles.xml
@@ -58,4 +58,22 @@
- 20sp
+
+
+
+
+
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 631fb194a7e..09f2ca270dc 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -29,5 +29,8 @@
#ff9a9a9a
#ff666666
#ff9a9a9a
+
+ #20ffffff
+ #ff99cc00
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index ace6756382f..53947435933 100755
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -19,4 +19,13 @@
32dip
220dip
16dip
+
+ 56dip
+ 64dip
+ 72dip
+ 48dip
+ 48dip
+ 16dip
+ 26dip
+ 40dip
diff --git a/res/values/strings.xml b/res/values/strings.xml
index af465953046..04eec1d1f2a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -2641,6 +2641,15 @@ found in the list of installed applications.
Accessibility services
Allow accessibility services
+
+ Enable touch exploration mode
+
+ Allows you to touch the screen to hear the contents under your finger.
+
+ Touch exploration mode changes the way your
+ device handles touch input. Would you like to take a short tutorial on using touch exploration?
No installed accessibility services.
Permanently remove the user CA certificate?
+
+ Touch exploration tutorial
+
+ Next
+
+ Back
+
+ Finish
+
+ Skip
+
+
+ Exploring the screen
+
+ When touch exploration is enabled, you can touch the screen to hear spoken descriptions of the content under your finger. The screen currently contains a list of application icons. Find one of them by placing a finger on the screen and sliding it around.
+
+ Good, keep your finger on the screen and keep sliding around to find at least one more icon.
+
+ When you\'ve found a widget that you want to click on, you can tap once to activate it. Slide your finger to find the icon for %s.
+
+ Your finger is on top of the %s icon. Tap once to activate the icon.
+
+ You\'ve entered and left the %s icon. Slowly explore again to find the icon while keeping your finger on the screen.
+
+ Good. To move to the next section, find and activate the button labeled \"%s\" that\'s located near the bottom-right of the screen.
+
+
+ Scrolling using two fingers
+
+ To perform single-finger gestures like scrolling, you can place two fingers on the screen. The screen currently contains a scrollable list of application names. First, try exploring a few of the list items using one finger.
+
+ Good, keep sliding your finger around to find at least one more list item.
+
+ Now scroll through the list by placing two fingers on a list item and sliding your fingers up. If you reach the top of the screen, you can place your fingers back on the list and continue.
+
+ Good, keep sliding your fingers up to scroll some more.
+
+ You have completed the touch exploration tutorial. To exit, find and click the button labeled \"%s.\"
+
diff --git a/res/values/styles.xml b/res/values/styles.xml
index d7f8d9bf0ba..d214d48b6af 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -158,4 +158,31 @@
- @layout/preference_inputmethod
- @layout/preference_inputmethod_widget
+
+
+
+
+
+
+
+
diff --git a/src/com/android/settings/AccessibilityTutorialActivity.java b/src/com/android/settings/AccessibilityTutorialActivity.java
new file mode 100644
index 00000000000..be9b90d05c1
--- /dev/null
+++ b/src/com/android/settings/AccessibilityTutorialActivity.java
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * 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;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.AbsListView;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.GridView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.ViewAnimator;
+
+import java.util.List;
+
+/**
+ * This class provides a short tutorial that introduces the user to the features
+ * available in Touch Exploration.
+ */
+public class AccessibilityTutorialActivity extends Activity {
+ /** Intent action for launching this activity. */
+ public static final String ACTION = "com.android.settings.touchtutorial.LAUNCH_TUTORIAL";
+
+ /** Instance state saving constant for the active module. */
+ private static final String KEY_ACTIVE_MODULE = "active_module";
+
+ /** The index of the module to show when first opening the tutorial. */
+ private static final int DEFAULT_MODULE = 0;
+
+ /** View animator for switching between modules. */
+ private ViewAnimator mViewAnimator;
+
+ private AccessibilityManager mAccessibilityManager;
+
+ private final AnimationListener mInAnimationListener = new AnimationListener() {
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ final int index = mViewAnimator.getDisplayedChild();
+ final TutorialModule module = (TutorialModule) mViewAnimator.getChildAt(index);
+
+ activateModule(module);
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onAnimationStart(Animation animation) {
+ // Do nothing.
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Animation inAnimation = AnimationUtils.loadAnimation(this,
+ android.R.anim.slide_in_left);
+ inAnimation.setAnimationListener(mInAnimationListener);
+
+ final Animation outAnimation = AnimationUtils.loadAnimation(this,
+ android.R.anim.slide_in_left);
+
+ mViewAnimator = new ViewAnimator(this);
+ mViewAnimator.setInAnimation(inAnimation);
+ mViewAnimator.setOutAnimation(outAnimation);
+ mViewAnimator.addView(new TouchTutorialModule1(this, this));
+ mViewAnimator.addView(new TouchTutorialModule2(this, this));
+
+ setContentView(mViewAnimator);
+
+ mAccessibilityManager = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
+
+ if (savedInstanceState != null) {
+ show(savedInstanceState.getInt(KEY_ACTIVE_MODULE, DEFAULT_MODULE));
+ } else {
+ show(DEFAULT_MODULE);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(KEY_ACTIVE_MODULE, mViewAnimator.getDisplayedChild());
+ }
+
+ private void activateModule(TutorialModule module) {
+ module.activate();
+ }
+
+ private void deactivateModule(TutorialModule module) {
+ mAccessibilityManager.interrupt();
+ mViewAnimator.setOnKeyListener(null);
+ module.deactivate();
+ }
+
+ private void interrupt() {
+ mAccessibilityManager.interrupt();
+ }
+
+ private void next() {
+ show(mViewAnimator.getDisplayedChild() + 1);
+ }
+
+ private void previous() {
+ show(mViewAnimator.getDisplayedChild() - 1);
+ }
+
+ private void show(int which) {
+ if ((which < 0) || (which >= mViewAnimator.getChildCount())) {
+ return;
+ }
+
+ mAccessibilityManager.interrupt();
+
+ final int displayedIndex = mViewAnimator.getDisplayedChild();
+ final TutorialModule displayedView = (TutorialModule) mViewAnimator.getChildAt(
+ displayedIndex);
+ deactivateModule(displayedView);
+
+ mViewAnimator.setDisplayedChild(which);
+ }
+
+ /**
+ * Loads application labels and icons.
+ */
+ private static class AppsAdapter extends ArrayAdapter {
+ protected final int mTextViewResourceId;
+
+ private final int mIconSize;
+ private final View.OnHoverListener mDefaultHoverListener;
+
+ private View.OnHoverListener mHoverListener;
+
+ public AppsAdapter(Context context, int resource, int textViewResourceId) {
+ super(context, resource, textViewResourceId);
+
+ mIconSize = context.getResources().getDimensionPixelSize(R.dimen.app_icon_size);
+ mTextViewResourceId = textViewResourceId;
+ mDefaultHoverListener = new View.OnHoverListener() {
+ @Override
+ public boolean onHover(View v, MotionEvent event) {
+ if (mHoverListener != null) {
+ return mHoverListener.onHover(v, event);
+ } else {
+ return false;
+ }
+ }
+ };
+
+ loadAllApps();
+ }
+
+ public CharSequence getLabel(int position) {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final ResolveInfo appInfo = getItem(position);
+ return appInfo.loadLabel(packageManager);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ final PackageManager packageManager = getContext().getPackageManager();
+ final View view = super.getView(position, convertView, parent);
+ view.setOnHoverListener(mDefaultHoverListener);
+ view.setTag(position);
+
+ final ResolveInfo appInfo = getItem(position);
+ final CharSequence label = appInfo.loadLabel(packageManager);
+ final Drawable icon = appInfo.loadIcon(packageManager);
+ final TextView text = (TextView) view.findViewById(mTextViewResourceId);
+
+ icon.setBounds(0, 0, mIconSize, mIconSize);
+
+ populateView(text, label, icon);
+
+ return view;
+ }
+
+ private void loadAllApps() {
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ final PackageManager pm = getContext().getPackageManager();
+ final List apps = pm.queryIntentActivities(mainIntent, 0);
+
+ addAll(apps);
+ }
+
+ protected void populateView(TextView text, CharSequence label, Drawable icon) {
+ text.setText(label);
+ text.setCompoundDrawables(null, icon, null, null);
+ }
+
+ public void setOnHoverListener(View.OnHoverListener hoverListener) {
+ mHoverListener = hoverListener;
+ }
+ }
+
+ /**
+ * Introduces using a finger to explore and interact with on-screen content.
+ */
+ private static class TouchTutorialModule1 extends TutorialModule implements
+ View.OnHoverListener, AdapterView.OnItemClickListener {
+ /**
+ * Handles the case where the user overshoots the target area.
+ */
+ private class HoverTargetHandler extends Handler {
+ private static final int MSG_ENTERED_TARGET = 1;
+ private static final int DELAY_ENTERED_TARGET = 500;
+
+ private boolean mInsideTarget = false;
+
+ public void enteredTarget() {
+ mInsideTarget = true;
+ mHandler.sendEmptyMessageDelayed(MSG_ENTERED_TARGET, DELAY_ENTERED_TARGET);
+ }
+
+ public void exitedTarget() {
+ mInsideTarget = false;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_ENTERED_TARGET:
+ if (mInsideTarget) {
+ addInstruction(R.string.accessibility_tutorial_lesson_1_text_4,
+ mTargetName);
+ } else {
+ addInstruction(R.string.accessibility_tutorial_lesson_1_text_4_exited,
+ mTargetName);
+ setFlag(FLAG_TOUCHED_TARGET, false);
+ }
+ break;
+ }
+ }
+ }
+
+ private static final int FLAG_TOUCH_ITEMS = 0x1;
+ private static final int FLAG_TOUCHED_ITEMS = 0x2;
+ private static final int FLAG_TOUCHED_TARGET = 0x4;
+ private static final int FLAG_TAPPED_TARGET = 0x8;
+
+ private static final int MORE_EXPLORED_COUNT = 1;
+ private static final int DONE_EXPLORED_COUNT = 2;
+
+ private final HoverTargetHandler mHandler;
+ private final AppsAdapter mAppsAdapter;
+ private final GridView mAllApps;
+
+ private int mTouched = 0;
+
+ private int mTargetPosition;
+ private CharSequence mTargetName;
+
+ public TouchTutorialModule1(Context context, AccessibilityTutorialActivity controller) {
+ super(context, controller, R.layout.accessibility_tutorial_1,
+ R.string.accessibility_tutorial_lesson_1_title);
+
+ mHandler = new HoverTargetHandler();
+
+ mAppsAdapter = new AppsAdapter(context, R.layout.accessibility_tutorial_app_icon,
+ R.id.app_icon);
+ mAppsAdapter.setOnHoverListener(this);
+
+ mAllApps = (GridView) findViewById(R.id.all_apps);
+ mAllApps.setAdapter(mAppsAdapter);
+ mAllApps.setOnItemClickListener(this);
+
+ findViewById(R.id.next_button).setOnHoverListener(this);
+
+ setPreviousVisible(false);
+ }
+
+ @Override
+ public boolean onHover(View v, MotionEvent event) {
+ switch (v.getId()) {
+ case R.id.app_icon:
+ if (hasFlag(FLAG_TOUCH_ITEMS) && !hasFlag(FLAG_TOUCHED_ITEMS) && v.isEnabled()
+ && (event.getAction() == MotionEvent.ACTION_HOVER_ENTER)) {
+ mTouched++;
+
+ if (mTouched >= DONE_EXPLORED_COUNT) {
+ setFlag(FLAG_TOUCHED_ITEMS, true);
+ addInstruction(R.string.accessibility_tutorial_lesson_1_text_3,
+ mTargetName);
+ } else if (mTouched == MORE_EXPLORED_COUNT) {
+ addInstruction(R.string.accessibility_tutorial_lesson_1_text_2_more);
+ }
+
+ v.setEnabled(false);
+ } else if (hasFlag(FLAG_TOUCHED_ITEMS)
+ && ((Integer) v.getTag() == mTargetPosition)) {
+ if (!hasFlag(FLAG_TOUCHED_TARGET)
+ && (event.getAction() == MotionEvent.ACTION_HOVER_ENTER)) {
+ mHandler.enteredTarget();
+ setFlag(FLAG_TOUCHED_TARGET, true);
+ } else if (event.getAction() == MotionEvent.ACTION_HOVER_EXIT) {
+ mHandler.exitedTarget();
+ }
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id) {
+ if (hasFlag(FLAG_TOUCHED_TARGET) && !hasFlag(FLAG_TAPPED_TARGET)
+ && (position == mTargetPosition)) {
+ setFlag(FLAG_TAPPED_TARGET, true);
+ final CharSequence nextText = getContext().getText(
+ R.string.accessibility_tutorial_next);
+ addInstruction(R.string.accessibility_tutorial_lesson_1_text_5, nextText);
+ }
+ }
+
+ @Override
+ public void onShown() {
+ final int first = mAllApps.getFirstVisiblePosition();
+ final int last = mAllApps.getLastVisiblePosition();
+
+ mTargetPosition = 0;
+ mTargetName = mAppsAdapter.getLabel(mTargetPosition);
+
+ addInstruction(R.string.accessibility_tutorial_lesson_1_text_1);
+ setFlag(FLAG_TOUCH_ITEMS, true);
+ }
+ }
+
+ /**
+ * Introduces using two fingers to scroll through a list.
+ */
+ private static class TouchTutorialModule2 extends TutorialModule implements
+ AbsListView.OnScrollListener, View.OnHoverListener {
+ private static final int FLAG_EXPLORE_LIST = 0x1;
+ private static final int FLAG_SCROLL_LIST = 0x2;
+ private static final int FLAG_COMPLETED_TUTORIAL = 0x4;
+
+ private static final int MORE_EXPLORE_COUNT = 1;
+ private static final int DONE_EXPLORE_COUNT = 2;
+ private static final int MORE_SCROLL_COUNT = 2;
+ private static final int DONE_SCROLL_COUNT = 4;
+
+ private final AppsAdapter mAppsAdapter;
+
+ private int mExploreCount = 0;
+ private int mInitialVisibleItem = -1;
+ private int mScrollCount = 0;
+
+ public TouchTutorialModule2(Context context, AccessibilityTutorialActivity controller) {
+ super(context, controller, R.layout.accessibility_tutorial_2,
+ R.string.accessibility_tutorial_lesson_2_title);
+
+ mAppsAdapter = new AppsAdapter(context, android.R.layout.simple_list_item_1,
+ android.R.id.text1) {
+ @Override
+ protected void populateView(TextView text, CharSequence label, Drawable icon) {
+ text.setText(label);
+ text.setCompoundDrawables(icon, null, null, null);
+ }
+ };
+ mAppsAdapter.setOnHoverListener(this);
+
+ ((ListView) findViewById(R.id.list_view)).setAdapter(mAppsAdapter);
+ ((ListView) findViewById(R.id.list_view)).setOnScrollListener(this);
+
+ setNextVisible(false);
+ setFinishVisible(true);
+ }
+
+ @Override
+ public boolean onHover(View v, MotionEvent e) {
+ if (e.getAction() != MotionEvent.ACTION_HOVER_ENTER) {
+ return false;
+ }
+
+ switch (v.getId()) {
+ case android.R.id.text1:
+ if (hasFlag(FLAG_EXPLORE_LIST) && !hasFlag(FLAG_SCROLL_LIST)) {
+ mExploreCount++;
+
+ if (mExploreCount >= DONE_EXPLORE_COUNT) {
+ addInstruction(R.string.accessibility_tutorial_lesson_2_text_3);
+ setFlag(FLAG_SCROLL_LIST, true);
+ } else if (mExploreCount == MORE_EXPLORE_COUNT) {
+ addInstruction(R.string.accessibility_tutorial_lesson_2_text_2_more);
+ }
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if (hasFlag(FLAG_SCROLL_LIST) && !hasFlag(FLAG_COMPLETED_TUTORIAL)) {
+ if (mInitialVisibleItem < 0) {
+ mInitialVisibleItem = firstVisibleItem;
+ }
+
+ final int scrollCount = Math.abs(mInitialVisibleItem - firstVisibleItem);
+
+ if ((mScrollCount == scrollCount) || (scrollCount <= 0)) {
+ return;
+ } else {
+ mScrollCount = scrollCount;
+ }
+
+ if (mScrollCount >= DONE_SCROLL_COUNT) {
+ final CharSequence finishText = getContext().getText(
+ R.string.accessibility_tutorial_finish);
+ addInstruction(R.string.accessibility_tutorial_lesson_2_text_4, finishText);
+ setFlag(FLAG_COMPLETED_TUTORIAL, true);
+ } else if (mScrollCount == MORE_SCROLL_COUNT) {
+ addInstruction(R.string.accessibility_tutorial_lesson_2_text_3_more);
+ }
+ }
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ // Do nothing.
+ }
+
+ @Override
+ public void onShown() {
+ addInstruction(R.string.accessibility_tutorial_lesson_2_text_1);
+ setFlag(FLAG_EXPLORE_LIST, true);
+ }
+ }
+
+ /**
+ * Abstract class that represents a single module within a tutorial.
+ */
+ private static abstract class TutorialModule extends FrameLayout implements OnClickListener {
+ private final AccessibilityTutorialActivity mController;
+ private final TextView mInstructions;
+ private final TextView mTitle;
+ private final Button mPrevious;
+ private final Button mNext;
+ private final Button mFinish;
+
+ /** Which bit flags have been set. */
+ private long mFlags;
+
+ /** Whether this module is currently focused. */
+ private boolean mIsVisible;
+
+ /**
+ * Constructs a new tutorial module for the given context and controller
+ * with the specified layout.
+ *
+ * @param context The parent context.
+ * @param controller The parent tutorial controller.
+ * @param layoutResId The layout to use for this module.
+ */
+ public TutorialModule(Context context, AccessibilityTutorialActivity controller,
+ int layoutResId, int titleResId) {
+ super(context);
+
+ mController = controller;
+
+ final View container = LayoutInflater.from(context).inflate(
+ R.layout.accessibility_tutorial_container, this, true);
+
+ mInstructions = (TextView) container.findViewById(R.id.instructions);
+ mTitle = (TextView) container.findViewById(R.id.title);
+ mTitle.setText(titleResId);
+ mPrevious = (Button) container.findViewById(R.id.back_button);
+ mPrevious.setOnClickListener(this);
+ mNext = (Button) container.findViewById(R.id.next_button);
+ mNext.setOnClickListener(this);
+ mFinish = (Button) container.findViewById(R.id.finish_button);
+ mFinish.setOnClickListener(this);
+
+ final ViewGroup contentHolder = (ViewGroup) container.findViewById(R.id.content);
+ LayoutInflater.from(context).inflate(layoutResId, contentHolder, true);
+ }
+
+ /**
+ * Called when this tutorial gains focus.
+ */
+ public final void activate() {
+ mIsVisible = true;
+
+ mFlags = 0;
+ mInstructions.setVisibility(View.GONE);
+ mTitle.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+
+ onShown();
+ }
+
+ /**
+ * Formats an instruction string and adds it to the speaking queue.
+ *
+ * @param resId The resource id of the instruction string.
+ * @param formatArgs Optional formatting arguments.
+ * @see String#format(String, Object...)
+ */
+ protected void addInstruction(final int resId, Object... formatArgs) {
+ if (!mIsVisible) {
+ return;
+ }
+
+ final String text = getContext().getString(resId, formatArgs);
+
+ mInstructions.setVisibility(View.VISIBLE);
+ mInstructions.setText(text);
+ mInstructions.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+
+ /**
+ * Called when this tutorial loses focus.
+ */
+ public void deactivate() {
+ mIsVisible = false;
+
+ mController.interrupt();
+ }
+
+ /**
+ * Returns {@code true} if the flag with the specified id has been set.
+ *
+ * @param flagId The id of the flag to check for.
+ * @return {@code true} if the flag with the specified id has been set.
+ */
+ protected boolean hasFlag(int flagId) {
+ return (mFlags & flagId) == flagId;
+ }
+
+ @Override
+ public void onClick(View v) {
+ switch (v.getId()) {
+ case R.id.back_button:
+ mController.previous();
+ break;
+ case R.id.next_button:
+ mController.next();
+ break;
+ case R.id.finish_button:
+ mController.finish();
+ break;
+ }
+ }
+
+ public abstract void onShown();
+
+ protected void setFinishVisible(boolean visible) {
+ mFinish.setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ /**
+ * Sets or removes the flag with the specified id.
+ *
+ * @param flagId The id of the flag to modify.
+ * @param value {@code true} to set the flag, {@code false} to remove
+ * it.
+ */
+ protected void setFlag(int flagId, boolean value) {
+ if (value) {
+ mFlags |= flagId;
+ } else {
+ mFlags = ~(~mFlags | flagId);
+ }
+ }
+
+ protected void setNextVisible(boolean visible) {
+ mNext.setVisibility(visible ? VISIBLE : GONE);
+ }
+
+ protected void setPreviousVisible(boolean visible) {
+ mPrevious.setVisibility(visible ? VISIBLE : GONE);
+ }
+ }
+}