diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index 8157e9fab..4e63dde0e 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -542,7 +542,7 @@ - + + + + + + + + diff --git a/samples/ApiDemos/_index.html b/samples/ApiDemos/_index.html index 281364c34..3c458436e 100644 --- a/samples/ApiDemos/_index.html +++ b/samples/ApiDemos/_index.html @@ -39,6 +39,10 @@ support
  • Window Querying Accessibility Service
  • +
  • Custom View + Accessibility
  • + 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 000000000..ceb6b6161 --- /dev/null +++ b/samples/ApiDemos/res/layout/custom_view_accessibility.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml index a31ad8a8f..2913844be 100644 --- a/samples/ApiDemos/res/values/strings.xml +++ b/samples/ApiDemos/res/values/strings.xml @@ -1296,7 +1296,7 @@ ClockBack 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 @@ Task App Accessibility Service 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. + Accessibility/Custom View + + 1. Enable TalkBack (Settings -> Accessibility -> TalkBack). + \n\n2. Enable Explore-by-Touch (Settings -> Accessibility -> Explore by Touch). + \n\n3. Touch explore/poke the buttons. + + On + Off + Task Task %1$s %2$s is complete 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 000000000..8facfbaad --- /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). + *

    + * 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. + *

    + */ +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. + *

    + * Note: 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}. + *

    + */ + 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. + *

    + * Note: 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. + *

    + */ + 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. + *

    + * Note: 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. + *

    + */ + 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 7506cb391..713d913a4 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. -
    +
    Window Querying Accessibility Service
    @@ -21,3 +21,12 @@ AccessibilityRecords.
    + +
    +
    Custom View Accessibility
    +
    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). +
    +