| /* |
| * 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(); |
| } |
| } |
| } |