diff options
| author | Alexander Lucas <alexlucas@google.com> | 2011-10-28 17:26:20 +0000 |
|---|---|---|
| committer | Android Git Automerger <android-git-automerger@android.com> | 2011-10-28 17:26:20 +0000 |
| commit | 1e94a78a1e06d680b23e01683a2dd8d0fa25df13 (patch) | |
| tree | 6e6e8b87044f149f47089f554a3cf874ad68c186 /samples | |
| parent | 060c9691a18b877718b21339af5468898e7900aa (diff) | |
| parent | 96c0210eb57e60f03c23a8e504782a8ba4b9edfe (diff) | |
| download | android_development-1e94a78a1e06d680b23e01683a2dd8d0fa25df13.tar.gz android_development-1e94a78a1e06d680b23e01683a2dd8d0fa25df13.tar.bz2 android_development-1e94a78a1e06d680b23e01683a2dd8d0fa25df13.zip | |
am 96c0210e: Adding ICS Accessibility Features to ApiDemos app.
* commit '96c0210eb57e60f03c23a8e504782a8ba4b9edfe':
Adding ICS Accessibility Features to ApiDemos app.
Diffstat (limited to 'samples')
10 files changed, 457 insertions, 4 deletions
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index fd591079c..064d05887 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -36,7 +36,7 @@ <!-- For android.media.audiofx.Visualizer --> <uses-permission android:name="android.permission.RECORD_AUDIO" /> - <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="13" /> + <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="14" /> <!-- We will request access to the camera, saying we require a camera of some sort but not one with autofocus capability. --> @@ -242,7 +242,7 @@ <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity> - + <!-- Fragment Samples --> <activity android:name=".app.FragmentAlertDialog" @@ -541,6 +541,25 @@ </intent-filter> </activity> + <activity android:name=".accessibility.TaskListActivity" + android:label="@string/accessibility_taskapp_name" + android:enabled="@bool/atLeastIceCreamSandwich"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.SAMPLE_CODE" /> + </intent-filter> + </activity> + + <service android:name=".accessibility.TaskBackService" + android:label="@string/accessibility_service_label" + android:enabled="@bool/atLeastIceCreamSandwich"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService" /> + </intent-filter> + <meta-data android:name="android.accessibilityservice" android:resource="@xml/taskbackconfig" /> + </service> + + <!-- Instrumentation Samples --> <activity android:name=".app.LocalSample" android:label="@string/activity_local_sample"> @@ -2237,7 +2256,7 @@ <category android:name="android.intent.category.SAMPLE_CODE" /> </intent-filter> </activity> - + <activity android:name=".graphics.TriangleActivity" android:label="Graphics/OpenGL ES/Textured Triangle" android:theme="@android:style/Theme.Holo.Dialog" diff --git a/samples/ApiDemos/_index.html b/samples/ApiDemos/_index.html index 9466c95b5..281364c34 100644 --- a/samples/ApiDemos/_index.html +++ b/samples/ApiDemos/_index.html @@ -36,6 +36,9 @@ fragment animations</a></li> <li><a href="src/com/example/android/apis/graphics/TouchPaint.html">Stylus and hover support</a></li> <li><a href="src/com/example/android/apis/view/Switches.html">Switch widget</a></li> +<li><a + href="src/com/example/android/apis/accessibility/TaskBackService.html">Window + Querying Accessibility Service</a></li> </ul> </div> diff --git a/samples/ApiDemos/res/layout/tasklist_main.xml b/samples/ApiDemos/res/layout/tasklist_main.xml new file mode 100644 index 000000000..61a17e3ef --- /dev/null +++ b/samples/ApiDemos/res/layout/tasklist_main.xml @@ -0,0 +1,28 @@ +<?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_width="fill_parent" + android:layout_height="fill_parent"> + <com.example.android.apis.accessibility.TaskListView + android:id="@android:id/list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_weight="1" + android:drawSelectorOnTop="false" /> +</LinearLayout> diff --git a/samples/ApiDemos/res/layout/tasklist_row.xml b/samples/ApiDemos/res/layout/tasklist_row.xml new file mode 100644 index 000000000..51bb31eea --- /dev/null +++ b/samples/ApiDemos/res/layout/tasklist_row.xml @@ -0,0 +1,34 @@ +<?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="horizontal" + android:layout_width="match_parent" + android:layout_height="match_parent" android:weightSum="1" android:baselineAligned="false"> + <TextView android:id="@+id/tasklist_label" + android:textSize="20dp" + android:textStyle="bold" + android:layout_height="48dp" + android:layout_width="300dip" + /> + + <CheckBox android:id="@+id/tasklist_finished" + android:checked="false" android:layout_height="48dp" + android:layout_weight=".25" + android:layout_width="wrap_content" /> +</LinearLayout> diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index 5a7183c6d..1dfc1bb48 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -1286,4 +1286,18 @@ <string name="dismiss">Dismiss</string> <string name="share">Share</string> -</resources> + + <!-- ============================ --> + <!-- Accessibility examples strings --> + <!-- ============================ --> + + + <string name="accessibility_taskapp_name">Accessibility/Accessibility Node Querying</string> + <string name="accessibility_service_description">Task App Accessibility Service</string> + <string name="accessibility_service_label">TaskBack</string> + + <string name="task_name">Task</string> + <string name="task_complete_template">Task %1$s %2$s</string> + <string name="task_complete">is complete</string> + <string name="task_not_complete">is not complete</string> + </resources> diff --git a/samples/ApiDemos/res/xml/taskbackconfig.xml b/samples/ApiDemos/res/xml/taskbackconfig.xml new file mode 100644 index 000000000..02ff11c70 --- /dev/null +++ b/samples/ApiDemos/res/xml/taskbackconfig.xml @@ -0,0 +1,25 @@ +<?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. + --> + + <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" + android:accessibilityEventTypes="typeViewClicked|typeViewFocused" + android:packageNames="com.example.android.apis" + android:accessibilityFeedbackType="feedbackSpoken" + android:notificationTimeout="100" + android:accessibilityFlags="flagDefault" + android:canRetrieveWindowContent="true" + android:description="@string/accessibility_service_description" /> diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskBackService.java b/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskBackService.java new file mode 100644 index 000000000..dbbfe3a1b --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskBackService.java @@ -0,0 +1,160 @@ +/* + * 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. + */ + +package com.example.android.apis.accessibility; + +import com.example.android.apis.R; + +import android.accessibilityservice.AccessibilityService; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityRecord; +import android.speech.tts.TextToSpeech; +import android.speech.tts.TextToSpeech.OnInitListener; + +import java.util.Locale; + +/** The TaskBackService listens for AccessibilityEvents, and turns them into information it can + * communicate to the user with speech. + */ +public class TaskBackService extends AccessibilityService implements OnInitListener { + + private final String LOG_TAG = "TaskBackService/onAccessibilityEvent"; + private boolean mTextToSpeechInitialized = false; + private TextToSpeech mTts = null; + private static final String SEPARATOR = ", "; + + + + /** Initializes the Text-To-Speech engine as soon as the service is connected. */ + @Override + public void onServiceConnected() { + mTts = new TextToSpeech(getApplicationContext(), this); + } + + /** Processes an AccessibilityEvent, by traversing the View's tree and putting together a + * message to speak to the user. + */ + @Override + public void onAccessibilityEvent(AccessibilityEvent event) { + if (!mTextToSpeechInitialized) { + Log.e(LOG_TAG, "Text-To-Speech engine not ready. Bailing out."); + return; + } + + int eventType = event.getEventType(); + if (eventType != AccessibilityEvent.TYPE_VIEW_CLICKED) { + return; + } + + /* This AccessibilityNodeInfo represents the view that fired the + * AccessibilityEvent. The following code will use it to traverse + * the view hierarchy, using this node as a starting point. + */ + AccessibilityNodeInfo entryNode = event.getSource(); + + /* Every method that returns an AccessibilityNodeInfo may return null, + * because the explored window is in another process and the corresponding + * View might be gone by the time your request reaches the view hierarchy." + */ + if (entryNode == null) { + return; + } + // Grab the parent of the view that fired the event. + AccessibilityNodeInfo rowNode = entryNode.getParent(); + + if (rowNode == null) { + return; + } + + /* Using this parent, get references to both child nodes, + * the label and the checkbox. + */ + AccessibilityNodeInfo labelNode = rowNode.getChild(0); + AccessibilityNodeInfo completeNode = rowNode.getChild(1); + + if (labelNode == null || completeNode == null) { + return; + } + + /* Using these to determine what the task is and whether or not + * it's complete, based on the text inside the label, and the state + * of the checkbox. + */ + + // Quick check to make sure we're not in the ApiDemos nav. + if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) { + return; + } + + CharSequence taskLabel = labelNode.getText(); + boolean isComplete = completeNode.isChecked(); + + String completeStr = null;; + if (isComplete) { + completeStr = getString(R.string.task_complete); + } else { + completeStr = getString(R.string.task_not_complete); + } + + String taskStr = getString(R.string.task_complete_template, taskLabel, completeStr); + StringBuilder forSpeech = new StringBuilder(taskStr); + + /* The custom listview added extra context to the event by adding + * an AccessibilityRecord to it. Extract that from the event and read it. + */ + int records = event.getRecordCount(); + + for (int i = 0; i < records; i++) { + AccessibilityRecord record = event.getRecord(i); + CharSequence contentDescription = record.getContentDescription(); + if (contentDescription != null) { + forSpeech.append(SEPARATOR).append(contentDescription); + } + } + + /* Speak the forSpeech string to the user. QUEUE_ADD adds the string to the end of the + * queue, QUEUE_FLUSH would interrupt whatever was currently being said. + */ + mTts.speak(forSpeech.toString() , TextToSpeech.QUEUE_ADD, null); + Log.d(LOG_TAG, forSpeech.toString()); + } + + @Override + public void onInterrupt() { + /* do nothing */ + } + + /** Sets a flag so that the TaskBackService knows that the Text-To-Speech engine has been + * initialized, and can now handle speaking requests. + */ + @Override + public void onInit (int status) { + if (status == TextToSpeech.SUCCESS) { + mTts.setLanguage(Locale.US); + mTextToSpeechInitialized = true; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mTextToSpeechInitialized) { + mTts.shutdown(); + } + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskListActivity.java b/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskListActivity.java new file mode 100644 index 000000000..bbc1403b8 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskListActivity.java @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package com.example.android.apis.accessibility; + +import com.example.android.apis.R; + +import android.app.ListActivity; +import android.os.Bundle; +import android.widget.ArrayAdapter; + +/** Starts up the task list that will interact with the AccessibilityService sample. */ +public class TaskListActivity extends ListActivity { + /** Called when the activity is first created. */ + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.tasklist_main); + + // Hardcoded hand-waving here. + boolean[] checkboxes = {true, true, false, true, false, false, false}; + String[] labels = {"Take out Trash", "Do Laundry", + "Conquer World", "Nap", "Do Taxes", + "Abolish IRS", "Tea with Aunt Sharon" }; + + TaskAdapter myAdapter = new TaskAdapter(this, labels, checkboxes); + this.setListAdapter(myAdapter); + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskListView.java b/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskListView.java new file mode 100644 index 000000000..a4b17cb19 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/TaskListView.java @@ -0,0 +1,119 @@ +/* + * 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. + */ + +package com.example.android.apis.accessibility; + +import com.example.android.apis.R; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.ListView; +import android.widget.TextView; + + +/** Acts as a go-between for all AccessibilityEvents sent from items in the ListView, providing the + * option of sending more context to an AccessibilityService by adding more AccessiblityRecords to + * an event. + */ +public class TaskListView extends ListView { + + public TaskListView(Context context, AttributeSet attributeSet) { + super(context, attributeSet); + } + + /** + * This method will fire whenever a child event wants to send an AccessibilityEvent. As a + * result, it's a great place to add more AccessibilityRecords, if you want. In this case, + * the code is grabbing the position of the item in the list, and assuming that to be the + * priority for the task. + */ + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + // Add a record for ourselves as well. + AccessibilityEvent record = AccessibilityEvent.obtain(); + super.onInitializeAccessibilityEvent(record); + + int priority = (Integer) child.getTag(); + String priorityStr = "Priority: " + priority; + record.setContentDescription(priorityStr); + + event.appendRecord(record); + return true; + } +} + +/** Adds Accessibility information to individual child views of rows in the list. */ +final class TaskAdapter extends BaseAdapter{ + + private String[] mLabels = null; + private boolean[] mCheckboxes = null; + private Context mContext = null; + + public TaskAdapter(Context context, String[] labels, boolean[] checkboxes) { + super(); + mContext = context; + mLabels = labels; + mCheckboxes = checkboxes; + } + + @Override + public int getCount() { + return mLabels.length; + } + + /** Expands the views for individual list entries, and sets content descriptions for use by the + * TaskBackAccessibilityService. + */ + @Override + public View getView(int position, View convertView, ViewGroup parent) { + if(convertView == null) { + LayoutInflater inflater = LayoutInflater.from(mContext); + convertView = inflater.inflate(R.layout.tasklist_row, parent, false); + } + + CheckBox checkbox = (CheckBox) convertView.findViewById(R.id.tasklist_finished); + checkbox.setChecked(mCheckboxes[position]); + + TextView label = (TextView)(convertView.findViewById(R.id.tasklist_label)); + label.setText(mLabels[position]); + + String contentDescription = new StringBuilder() + .append(mContext.getString(R.string.task_name)) + .append(' ') + .append(mLabels[position]).toString(); + label.setContentDescription(contentDescription); + + convertView.setTag(position); + + return convertView; + } + + @Override + public Object getItem(int position) { + return mLabels[position]; + } + + @Override + public long getItemId(int position) { + return position; + } +} diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html new file mode 100644 index 000000000..eacefa10a --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html @@ -0,0 +1,9 @@ +<dl> + <dt><a href="TaskBackService.html">Window Querying Accessibility Service</a></dt> + <dd>Demonstrates several new accessibility features in Ice Cream Sandwich, + including the ability for an AccessibilityService to traverse the view + hierarchy using AccessibilityNodeInfo objects, service configuration via + xml files, and adding additional information to AccessibilityEvents using + AccessibilityRecords. + </dd> +</dl> |
