Adding a code sample for implementing accessibility in a custom view.
bug:5683532
Change-Id: I79471c431ba3aeb4abe57f850e84a4da0bc1feba
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 8157e9f..4e63dde 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -542,7 +542,7 @@
</activity>
<!-- ============================ -->
- <!-- Accessibility examples strings -->
+ <!-- Accessibility examples -->
<!-- ============================ -->
<activity android:name=".accessibility.ClockBackActivity"
@@ -580,6 +580,15 @@
android:resource="@xml/taskbackconfig" />
</service>
+ <activity android:name=".accessibility.CustomViewAccessibilityActivity"
+ android:label="@string/accessibility_custom_view"
+ android:enabled="@bool/atLeastIceCreamSandwich">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.SAMPLE_CODE" />
+ </intent-filter>
+ </activity>
+
<!-- Instrumentation Samples -->
<activity android:name=".app.LocalSample" android:label="@string/activity_local_sample">
diff --git a/samples/ApiDemos/_index.html b/samples/ApiDemos/_index.html
index 281364c..3c45843 100644
--- a/samples/ApiDemos/_index.html
+++ b/samples/ApiDemos/_index.html
@@ -39,6 +39,10 @@
<li><a
href="src/com/example/android/apis/accessibility/TaskBackService.html">Window
Querying Accessibility Service</a></li>
+<li><a
+ href="src/com/example/android/apis/accessibility/CustomViewAccessibilityActivity.html">Custom View
+ Accessibility</a></li>
+
</ul>
</div>
diff --git a/samples/ApiDemos/res/layout/custom_view_accessibility.xml b/samples/ApiDemos/res/layout/custom_view_accessibility.xml
new file mode 100644
index 0000000..ceb6b61
--- /dev/null
+++ b/samples/ApiDemos/res/layout/custom_view_accessibility.xml
@@ -0,0 +1,44 @@
+<?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_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="50dip"
+ android:text="@string/accessibility_custom_view_instructions">
+ </TextView>
+
+ <view
+ class="com.example.android.apis.accessibility.CustomViewAccessibilityActivity$AccessibleCompoundButtonInheritance"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="20dip"
+ android:clickable="true" >
+ </view>
+
+ <view
+ class="com.example.android.apis.accessibility.CustomViewAccessibilityActivity$AccessibleCompoundButtonComposition"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:padding="20dip"
+ android:clickable="true" >
+ </view>
+
+</LinearLayout>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index a31ad8a..2913844 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -1296,7 +1296,7 @@
<string name="accessibility_service_label">ClockBack</string>
<string name="accessibility_service_instructions">
1. Enable TalkBack (Settings -> Accessibility -> TalkBack).
- \n\n2. Enable Explore-byTouch (Settings -> Accessibility -> Explore by Touch).
+ \n\n2. Enable Explore-by-Touch (Settings -> Accessibility -> Explore by Touch).
\n\n3. Touch explore the Clock application and the home screen.
\n\n4. Go to the Clock application and change the time of an alarm.
\n\n5. Enable ClockBack (Settings -> Accessibility -> ClockBack).
@@ -1316,10 +1316,19 @@
<string name="accessibility_query_window_description">Task App Accessibility Service</string>
<string name="accessibility_query_window_instructions">
1. Enable QueryBack (Settings -> Accessibility -> QueryBack).
- \n\n2. Enable Explore-byTouch (Settings -> Accessibility -> Explore by Touch).
+ \n\n2. Enable Explore-by-Touch (Settings -> Accessibility -> Explore by Touch).
\n\n3. Touch explore the list.
</string>
+ <string name="accessibility_custom_view">Accessibility/Custom View</string>
+ <string name="accessibility_custom_view_instructions">
+ 1. Enable TalkBack (Settings -> Accessibility -> TalkBack).
+ \n\n2. Enable Explore-by-Touch (Settings -> Accessibility -> Explore by Touch).
+ \n\n3. Touch explore/poke the buttons.
+ </string>
+ <string name="accessibility_custom_on">On</string>
+ <string name="accessibility_custom_off">Off</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>
diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/CustomViewAccessibilityActivity.java b/samples/ApiDemos/src/com/example/android/apis/accessibility/CustomViewAccessibilityActivity.java
new file mode 100644
index 0000000..8facfba
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/CustomViewAccessibilityActivity.java
@@ -0,0 +1,280 @@
+/*
+ * 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 android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.example.android.apis.R;
+
+/**
+ * Demonstrates how to implement accessibility support of custom views. Custom view
+ * is a tailored widget developed by extending the base classes in the android.view
+ * package. This sample shows how to implement the accessibility behavior via both
+ * inheritance (non backwards compatible) and composition (backwards compatible).
+ * <p>
+ * While the Android framework has a diverse portfolio of views tailored for various
+ * use cases, sometimes a developer needs a specific functionality not implemented
+ * by the standard views. A solution is to write a custom view that extends one the
+ * base view classes. While implementing the desired functionality a developer should
+ * also implement accessibility support for that new functionality such that
+ * disabled users can leverage it.
+ * </p>
+ */
+public class CustomViewAccessibilityActivity extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.custom_view_accessibility);
+ }
+
+ /**
+ * Demonstrates how to enhance the accessibility support via inheritance.
+ * <p>
+ * <strong>Note:</strong> Using inheritance may break your application's
+ * backwards compatibility. In particular, overriding a method that takes as
+ * an argument or returns a class not present on an older platform
+ * version will prevent your application from running on that platform.
+ * For example, {@link AccessibilityNodeInfo} was introduced in
+ * {@link Build.VERSION_CODES#ICE_CREAM_SANDWICH API 14}, thus overriding
+ * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
+ * View.onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)}
+ * will prevent you application from running on a platform older than
+ * {@link Build.VERSION_CODES#ICE_CREAM_SANDWICH API 14}.
+ * </p>
+ */
+ public static class AccessibleCompoundButtonInheritance extends BaseToggleButton {
+
+ public AccessibleCompoundButtonInheritance(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+ // We called the super implementation to let super classes
+ // set appropriate event properties. Then we add the new property
+ // (checked) which is not supported by a super class.
+ event.setChecked(isChecked());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(info);
+ // We called the super implementation to let super classes set
+ // appropriate info properties. Then we add our properties
+ // (checkable and checked) which are not supported by a super class.
+ info.setCheckable(true);
+ info.setChecked(isChecked());
+ // Very often you will need to add only the text on the custom view.
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ info.setText(text);
+ }
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(event);
+ // We called the super implementation to populate its text to the
+ // event. Then we add our text not present in a super class.
+ // Very often you will need to add only the text on the custom view.
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
+ }
+ }
+ }
+
+ /**
+ * Demonstrates how to enhance the accessibility support via composition.
+ * <p>
+ * <strong>Note:</strong> Using composition ensures that your application is
+ * backwards compatible. The android-support-v4 library has API that allow
+ * using the accessibility APIs in a backwards compatible manner.
+ * </p>
+ */
+ public static class AccessibleCompoundButtonComposition extends BaseToggleButton {
+
+ public AccessibleCompoundButtonComposition(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ tryInstallAccessibilityDelegate();
+ }
+
+ public void tryInstallAccessibilityDelegate() {
+ // If the API version of the platform we are running is too old
+ // and does not support the AccessibilityDelegate APIs, do not
+ // call View.setAccessibilityDelegate(AccessibilityDelegate) or
+ // refer to AccessibilityDelegate, otherwise an exception will
+ // be thrown.
+ // NOTE: The android-support-v4 library contains APIs the enable
+ // using the accessibility APIs in a backwards compatible fashion.
+ if (Build.VERSION.SDK_INT < 14) {
+ return;
+ }
+ // AccessibilityDelegate allows clients to override its methods that
+ // correspond to the accessibility methods in View and register the
+ // delegate in the View essentially injecting the accessibility support.
+ setAccessibilityDelegate(new AccessibilityDelegate() {
+ @Override
+ public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(host, event);
+ // We called the super implementation to let super classes
+ // set appropriate event properties. Then we add the new property
+ // (checked) which is not supported by a super class.
+ event.setChecked(isChecked());
+ }
+
+ @Override
+ public void onInitializeAccessibilityNodeInfo(View host,
+ AccessibilityNodeInfo info) {
+ super.onInitializeAccessibilityNodeInfo(host, info);
+ // We called the super implementation to let super classes set
+ // appropriate info properties. Then we add our properties
+ // (checkable and checked) which are not supported by a super class.
+ info.setCheckable(true);
+ info.setChecked(isChecked());
+ // Very often you will need to add only the text on the custom view.
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ info.setText(text);
+ }
+ }
+
+ @Override
+ public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ super.onPopulateAccessibilityEvent(host, event);
+ // We called the super implementation to populate its text to the
+ // event. Then we add our text not present in a super class.
+ // Very often you will need to add only the text on the custom view.
+ CharSequence text = getText();
+ if (!TextUtils.isEmpty(text)) {
+ event.getText().add(text);
+ }
+ }
+ });
+ }
+ }
+
+ /**
+ * This is a base toggle button class whose accessibility is not tailored
+ * to reflect the new functionality it implements.
+ * <p>
+ * <strong>Note:</strong> This is not a sample implementation of a toggle
+ * button, rather a simple class needed to demonstrate how to refine the
+ * accessibility support of a custom View.
+ * </p>
+ */
+ private static class BaseToggleButton extends View {
+ private boolean mChecked;
+
+ private CharSequence mTextOn;
+ private CharSequence mTextOff;
+
+ private Layout mOnLayout;
+ private Layout mOffLayout;
+
+ private TextPaint mTextPaint;
+
+ public BaseToggleButton(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.buttonStyle);
+ }
+
+ public BaseToggleButton(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
+
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(android.R.attr.textSize, typedValue, true);
+ final int textSize = (int) typedValue.getDimension(
+ context.getResources().getDisplayMetrics());
+ mTextPaint.setTextSize(textSize);
+
+ context.getTheme().resolveAttribute(android.R.attr.textColorPrimary, typedValue, true);
+ final int textColor = context.getResources().getColor(typedValue.resourceId);
+ mTextPaint.setColor(textColor);
+
+ mTextOn = context.getString(R.string.accessibility_custom_on);
+ mTextOff = context.getString(R.string.accessibility_custom_off);
+ }
+
+ public boolean isChecked() {
+ return mChecked;
+ }
+
+ public CharSequence getText() {
+ return mChecked ? mTextOn : mTextOff;
+ }
+
+ @Override
+ public boolean performClick() {
+ final boolean handled = super.performClick();
+ if (!handled) {
+ mChecked ^= true;
+ invalidate();
+ }
+ return handled;
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mOnLayout == null) {
+ mOnLayout = makeLayout(mTextOn);
+ }
+ if (mOffLayout == null) {
+ mOffLayout = makeLayout(mTextOff);
+ }
+ final int minWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+ + getPaddingLeft() + getPaddingRight();
+ final int minHeight = Math.max(mOnLayout.getHeight(), mOffLayout.getHeight())
+ + getPaddingLeft() + getPaddingRight();
+ setMeasuredDimension(resolveSizeAndState(minWidth, widthMeasureSpec, 0),
+ resolveSizeAndState(minHeight, heightMeasureSpec, 0));
+ }
+
+ private Layout makeLayout(CharSequence text) {
+ return new StaticLayout(text, mTextPaint,
+ (int) Math.ceil(Layout.getDesiredWidth(text, mTextPaint)),
+ Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ canvas.save();
+ canvas.translate(getPaddingLeft(), getPaddingRight());
+ Layout switchText = mChecked ? mOnLayout : mOffLayout;
+ switchText.draw(canvas);
+ canvas.restore();
+ }
+ }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html
index 7506cb3..713d913 100644
--- a/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html
+++ b/samples/ApiDemos/src/com/example/android/apis/accessibility/_index.html
@@ -9,7 +9,7 @@
how to provide dynamic, context-dependent feedback — feedback type changes depending on
the ringer mode.
</dd>
-<dt>
+</dl>
<dl>
<dt><a href="TaskBackService.html">Window Querying Accessibility Service</a></dt>
@@ -21,3 +21,12 @@
AccessibilityRecords.
</dd>
</dl>
+
+<dl>
+ <dt><a href="CustomViewAccessibilityActivity.html">Custom View Accessibility</a></dt>
+ <dd>Demonstrates how to implement accessibility support of custom views. Custom view
+ is a tailored widget developed by extending the base classes in the android.view
+ package. This sample shows how to implement the accessibility behavior via both
+ inheritance (non backwards compatible) and composition (backwards compatible).
+ </dd>
+</dl>