Merge "Added touch exploration tutorial to Accessibility settings."

This commit is contained in:
Svetoslav Ganov
2011-07-12 17:45:45 -07:00
committed by Android (Google) Code Review
15 changed files with 1032 additions and 22 deletions

View File

@@ -943,6 +943,17 @@
</intent-filter>
</activity>
<!-- Accessibility tutorial -->
<activity android:name="AccessibilityTutorialActivity"
android:label="@string/accessibility_tutorial_title"
android:configChanges="orientation"
android:immersive="true"
android:theme="@android:style/Theme.Holo">
<intent-filter>
<action android:name="android.settings.ACCESSIBILITY_TUTORIAL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>

View File

@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:orientation="vertical"
android:paddingTop="@dimen/screen_margin_top"
android:paddingLeft="@dimen/screen_margin_sides"
android:paddingRight="@dimen/screen_margin_sides"
android:paddingBottom="@dimen/screen_margin_bottom">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minHeight="@dimen/title_height"
android:layout_marginLeft="@dimen/content_margin_left"
android:textAppearance="@style/AccessibilityTutorialTitle"
android:gravity="bottom"
android:clickable="false" />
<View
android:layout_marginTop="8dip"
android:layout_marginBottom="8dip"
android:layout_below="@id/title"
style="@style/AccessibilityTutorialDivider" />
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1.0"
android:orientation="vertical">
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
style="@style/AccessibilityTutorialDivider" />
<TextView
android:id="@+id/instructions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/description_margin_top"
android:layout_marginLeft="@dimen/description_margin_sides"
android:layout_marginRight="@dimen/description_margin_sides"
android:layout_marginBottom="16dip"
android:lineSpacingExtra="5sp"
android:textAppearance="?android:attr/textAppearanceMedium" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_marginBottom="16dip"
style="@style/AccessibilityTutorialDivider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="right">
<Button
android:id="@+id/back_button"
style="@style/AccessibilityTutorialButton"
android:text="@string/accessibility_tutorial_back" />
<Button
android:id="@+id/next_button"
style="@style/AccessibilityTutorialButton"
android:text="@string/accessibility_tutorial_next" />
<Button
android:id="@+id/finish_button"
style="@style/AccessibilityTutorialButton"
android:text="@string/accessibility_tutorial_finish"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<GridView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/all_apps"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="72dip"
android:numColumns="auto_fit"
android:verticalSpacing="10dip"
android:horizontalSpacing="10dip"
android:stretchMode="columnWidth" />

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

View File

@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_icon"
style="@style/AccessibilityTutorialIcon" />

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:paddingLeft="15dip"
android:paddingRight="15dip">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/AccessibilityTutorialTitle"
android:layout_marginTop="11dip" />
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:layout_gravity="center"
android:background="@color/divider_color"
android:layout_marginTop="14dip"
android:layout_marginBottom="13dip"
android:focusable="false"
android:clickable="false" />
</LinearLayout>
<LinearLayout
android:id="@+id/content"
android:orientation="vertical"
android:layout_height="0dip"
android:layout_width="fill_parent"
android:layout_weight="1.0"
android:paddingLeft="15dip"
android:paddingRight="15dip">
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:paddingLeft="15dip"
android:paddingRight="15dip">
<View
android:layout_width="wrap_content"
android:layout_height="1dip"
android:layout_gravity="center"
android:background="@android:drawable/divider_horizontal_dark"
android:layout_marginBottom="8dip"
android:focusable="false"
android:clickable="false" />
<TextView
android:id="@+id/instructions"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dip"
android:textAppearance="@style/AccessibilityTutorialBodyTextPrimary" />
</LinearLayout>
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:background="@android:drawable/bottom_bar">
<Button
android:id="@+id/back_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentLeft="true"
android:drawablePadding="3dip"
android:text="@string/accessibility_tutorial_back" />
<Button
android:id="@+id/next_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentRight="true"
android:drawablePadding="3dip"
android:text="@string/accessibility_tutorial_next" />
<Button
android:id="@+id/finish_button"
android:layout_width="150dip"
android:layout_height="wrap_content"
android:layout_margin="5dip"
android:layout_alignParentRight="true"
android:text="@string/accessibility_tutorial_finish"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>

View File

@@ -16,4 +16,6 @@
<resources>
<dimen name="screen_margin_sides">128dip</dimen>
<dimen name="screen_margin_top">72dip</dimen>
<dimen name="screen_margin_bottom">48dip</dimen>
</resources>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2010 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.
*/
-->
<resources>
<color name="divider_color">#20ffffff</color>
</resources>

View File

@@ -18,4 +18,10 @@
<dimen name="screen_margin_sides">40dip</dimen>
<dimen name="datetime_margin_top">40dip</dimen>
<dimen name="datetime_margin_bottom">40dip</dimen>
<dimen name="app_icon_size">72dip</dimen>
<dimen name="screen_margin_top">48dip</dimen>
<dimen name="screen_margin_bottom">48dip</dimen>
<dimen name="title_height">48dip</dimen>
<dimen name="content_margin_left">16dip</dimen>
<dimen name="description_margin_top">26dip</dimen>
</resources>

View File

@@ -58,4 +58,22 @@
<item name="android:textSize">20sp</item>
</style>
<style name="AccessibilityTutorialTitle" parent="@android:style/TextAppearance.Large">
<item name="android:textColor">@color/title_color</item>
</style>
<style name="AccessibilityTutorialButton">
<item name="android:layout_width">wrap_content</item>
<item name="android:minWidth">208dip</item>
<item name="android:layout_height">48dip</item>
<item name="android:paddingTop">0dip</item>
<item name="android:paddingLeft">0dip</item>
<item name="android:paddingRight">0dip</item>
<item name="android:paddingBottom">0dip</item>
<item name="android:textAppearance">?android:attr/textAppearanceMedium</item>
<item name="android:textSize">20dip</item>
</style>
<style name="AccessibilityTutorialBodyTextPrimary" parent="@android:style/TextAppearance.Medium">
</style>
</resources>

View File

@@ -29,5 +29,8 @@
<color name="crypt_keeper_clock_background">#ff9a9a9a</color>
<color name="crypt_keeper_clock_foreground">#ff666666</color>
<color name="crypt_keeper_clock_am_pm">#ff9a9a9a</color>
<color name="divider_color">#20ffffff</color>
<color name="title_color">#ff99cc00</color>
</resources>

View File

@@ -19,4 +19,13 @@
<dimen name="device_memory_usage_button_height">32dip</dimen>
<dimen name="data_usage_chart_height">220dip</dimen>
<dimen name="action_bar_switch_padding">16dip</dimen>
<dimen name="app_icon_size">56dip</dimen>
<dimen name="screen_margin_sides">64dip</dimen>
<dimen name="screen_margin_top">72dip</dimen>
<dimen name="screen_margin_bottom">48dip</dimen>
<dimen name="title_height">48dip</dimen>
<dimen name="content_margin_left">16dip</dimen>
<dimen name="description_margin_top">26dip</dimen>
<dimen name="description_margin_sides">40dip</dimen>
</resources>

View File

@@ -2641,6 +2641,15 @@ found in the list of installed applications.</string>
<string name="accessibility_services_category">Accessibility services</string>
<!-- Setting Checkbox title for enabling accessibility services [CHAR LIMIT=40] -->
<string name="toggle_accessibility_title">Allow accessibility services</string>
<!-- Setting Checkbox title for enabling touch exploration mode [CHAR LIMIT=40] -->
<string name="accessibility_touch_exploration_title">Enable touch exploration mode</string>
<!-- Setting Checkbox summary for enabling touch exploration mode [CHAR LIMIT=65] -->
<string name="accessibility_touch_exploration_summary">Allows you to touch the screen to hear the contents under your finger.</string>
<!-- Warning message describing changes in interaction from enabling touch exploration mode
and suggesting that the user goes through a tutorial, displayed as a dialog message the
first time the user selects to enable touch exploration -->
<string name="accessibility_touch_exploration_warning">Touch exploration mode changes the way your
device handles touch input. Would you like to take a short tutorial on using touch exploration?</string>
<!-- Message for announcing the lack of installed accessibility services. -->
<string name="no_accessibility_services_summary">No installed accessibility services.</string>
<!-- Warning message about security implications of enabling an accessibility service,
@@ -3503,4 +3512,43 @@ found in the list of installed applications.</string>
<!-- Alert dialog confirmation when removing a user CA certificate. -->
<string name="trusted_credentials_remove_confirmation">Permanently remove the user CA certificate?</string>
<!-- Title for the touch exploration tutorial. [CHAR LIMIT=40] -->
<string name="accessibility_tutorial_title">Touch exploration tutorial</string>
<!-- Button label to go to the next touch exploration tutorial lesson. [CHAR LIMIT=10] -->
<string name="accessibility_tutorial_next">Next</string>
<!-- Button label to go back to the previous touch explorationtutorial lesson. [CHAR LIMIT=10] -->
<string name="accessibility_tutorial_back">Back</string>
<!-- Button label to exit the touch explorationtutorial. [CHAR LIMIT=10] -->
<string name="accessibility_tutorial_finish">Finish</string>
<!-- Button label to skip the touch exploration tutorial. [CHAR LIMIT=10] -->
<string name="accessibility_tutorial_skip">Skip</string>
<!-- Title for touch exploration tutorial lesson 1. -->
<string name="accessibility_tutorial_lesson_1_title">Exploring the screen</string>
<!-- Instruction for touch exploration tutorial lesson 1. -->
<string name="accessibility_tutorial_lesson_1_text_1">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.</string>
<!-- Instruction for touch exploration tutorial lesson 1, displayed after the user touches one icon. -->
<string name="accessibility_tutorial_lesson_1_text_2_more">Good, keep your finger on the screen and keep sliding around to find at least one more icon.</string>
<!-- Instruction for touch exploration tutorial lesson 1, displayed after the user touches a second icon. -->
<string name="accessibility_tutorial_lesson_1_text_3">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 <xliff:g id="app_name" example="Browser">%s</xliff:g>.</string>
<!-- Instruction for touch exploration tutorial lesson 1, displayed if the user touched the target area and stays inside it. -->
<string name="accessibility_tutorial_lesson_1_text_4">Your finger is on top of the <xliff:g id="app_name" example="Browser">%s</xliff:g> icon. Tap once to activate the icon.</string>
<!-- Instruction for touch exploration tutorial lesson 1, displayed if the user touches inside the target area but moves outside. -->
<string name="accessibility_tutorial_lesson_1_text_4_exited">You\'ve entered and left the <xliff:g id="app_name" example="Browser">%s</xliff:g> icon. Slowly explore again to find the icon while keeping your finger on the screen.</string>
<!-- Instruction for touch exploration tutorial lesson 1, displayed after the user has tapped the target icon. -->
<string name="accessibility_tutorial_lesson_1_text_5">Good. To move to the next section, find and activate the button labeled \"<xliff:g id="next" example="Next">%s</xliff:g>\" that\'s located near the bottom-right of the screen.</string>
<!-- Title for touch exploration tutorial lesson 2. -->
<string name="accessibility_tutorial_lesson_2_title">Scrolling using two fingers</string>
<!-- Instruction for touch exploration tutorial lesson 2. -->
<string name="accessibility_tutorial_lesson_2_text_1">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.</string>
<!-- Instruction for touch exploration tutorial lesson 2, displayed after the user touches one list item. -->
<string name="accessibility_tutorial_lesson_2_text_2_more">Good, keep sliding your finger around to find at least one more list item.</string>
<!-- Instruction for touch exploration tutorial lesson 2, displayed after the user touches a second list item. -->
<string name="accessibility_tutorial_lesson_2_text_3">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.</string>
<!-- Instruction for touch exploration tutorial lesson 2, displayed after the user scrolls a small amount. -->
<string name="accessibility_tutorial_lesson_2_text_3_more">Good, keep sliding your fingers up to scroll some more.</string>
<!-- Instruction for touch exploration tutorial lesson 2, displayed after the user has scrolled a large amount. -->
<string name="accessibility_tutorial_lesson_2_text_4">You have completed the touch exploration tutorial. To exit, find and click the button labeled \"<xliff:g id="finish" example="Finish">%s</xliff:g>.\"</string>
</resources>

View File

@@ -158,4 +158,31 @@
<item name="android:layout">@layout/preference_inputmethod</item>
<item name="android:widgetLayout">@layout/preference_inputmethod_widget</item>
</style>
<style name="AccessibilityTutorialDivider">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">2dip</item>
<item name="android:gravity">fill_horizontal</item>
<item name="android:background">@color/divider_color</item>
</style>
<style name="AccessibilityTutorialTitle" parent="@android:style/TextAppearance.Large">
<item name="android:textColor">@color/title_color</item>
</style>
<style name="AccessibilityTutorialBodyTextPrimary" parent="@android:style/TextAppearance.Small">
</style>
<style name="AccessibilityTutorialIcon">
<item name="android:layout_width">96dip</item>
<item name="android:layout_height">96dip</item>
<item name="android:layout_gravity">center</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">marquee</item>
<item name="android:textSize">12dip</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:shadowRadius">2.0</item>
<item name="android:shadowColor">#B0000000</item>
</style>
</resources>

View File

@@ -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<ResolveInfo> {
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<ResolveInfo> 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);
}
}
}