diff options
author | Alan Viverette <alanv@google.com> | 2011-07-12 17:34:22 -0700 |
---|---|---|
committer | Alan Viverette <alanv@google.com> | 2011-07-12 17:34:22 -0700 |
commit | d21bfa152460c10e920bbb42a20c620e6195b923 (patch) | |
tree | 91c74170f5528f0281d1d89aaa542e9c805da331 | |
parent | 078b435743bf7f42feb140de287b70685cfcb7a8 (diff) | |
download | packages_apps_settings-d21bfa152460c10e920bbb42a20c620e6195b923.zip packages_apps_settings-d21bfa152460c10e920bbb42a20c620e6195b923.tar.gz packages_apps_settings-d21bfa152460c10e920bbb42a20c620e6195b923.tar.bz2 |
Added touch exploration tutorial to Accessibility settings.
Change-Id: Ic11f7c41e40de2b32f5815dbc3396a308c451c5b
-rw-r--r-- | AndroidManifest.xml | 11 | ||||
-rw-r--r-- | res/layout-sw600dp/accessibility_tutorial_container.xml | 107 | ||||
-rw-r--r-- | res/layout/accessibility_tutorial_1.xml | 26 | ||||
-rw-r--r-- | res/layout/accessibility_tutorial_2.xml | 21 | ||||
-rw-r--r-- | res/layout/accessibility_tutorial_app_icon.xml | 20 | ||||
-rw-r--r-- | res/layout/accessibility_tutorial_container.xml | 118 | ||||
-rwxr-xr-x | res/values-sw600dp-land/dimens.xml | 2 | ||||
-rw-r--r-- | res/values-sw600dp/colors.xml | 22 | ||||
-rwxr-xr-x | res/values-sw600dp/dimens.xml | 6 | ||||
-rw-r--r-- | res/values-sw600dp/styles.xml | 18 | ||||
-rw-r--r-- | res/values/colors.xml | 3 | ||||
-rwxr-xr-x | res/values/dimens.xml | 9 | ||||
-rw-r--r-- | res/values/strings.xml | 48 | ||||
-rw-r--r-- | res/values/styles.xml | 27 | ||||
-rw-r--r-- | src/com/android/settings/AccessibilityTutorialActivity.java | 616 |
15 files changed, 1032 insertions, 22 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 55fa851..77fd94e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -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> diff --git a/res/layout-sw600dp/accessibility_tutorial_container.xml b/res/layout-sw600dp/accessibility_tutorial_container.xml new file mode 100644 index 0000000..2895a29 --- /dev/null +++ b/res/layout-sw600dp/accessibility_tutorial_container.xml @@ -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>
\ 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 0000000..710e327 --- /dev/null +++ b/res/layout/accessibility_tutorial_1.xml @@ -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" />
\ 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 0000000..5f9c69d --- /dev/null +++ b/res/layout/accessibility_tutorial_2.xml @@ -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" />
\ 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 0000000..fe8399e --- /dev/null +++ b/res/layout/accessibility_tutorial_app_icon.xml @@ -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" />
\ 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 0000000..e949d2a --- /dev/null +++ b/res/layout/accessibility_tutorial_container.xml @@ -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>
\ No newline at end of file diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml index 9bd2336..994d4bb 100755 --- a/res/values-sw600dp-land/dimens.xml +++ b/res/values-sw600dp-land/dimens.xml @@ -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> diff --git a/res/values-sw600dp/colors.xml b/res/values-sw600dp/colors.xml deleted file mode 100644 index 4447333..0000000 --- a/res/values-sw600dp/colors.xml +++ /dev/null @@ -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> diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml index bc9f18d..9469a99 100755 --- a/res/values-sw600dp/dimens.xml +++ b/res/values-sw600dp/dimens.xml @@ -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> diff --git a/res/values-sw600dp/styles.xml b/res/values-sw600dp/styles.xml index 1e99e45..3403f3e 100644 --- a/res/values-sw600dp/styles.xml +++ b/res/values-sw600dp/styles.xml @@ -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> diff --git a/res/values/colors.xml b/res/values/colors.xml index 631fb19..09f2ca2 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -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> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index ace6756..5394743 100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -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> diff --git a/res/values/strings.xml b/res/values/strings.xml index af46595..04eec1d 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -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> diff --git a/res/values/styles.xml b/res/values/styles.xml index d7f8d9b..d214d48 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -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> diff --git a/src/com/android/settings/AccessibilityTutorialActivity.java b/src/com/android/settings/AccessibilityTutorialActivity.java new file mode 100644 index 0000000..be9b90d --- /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<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); + } + } +} |