diff options
author | svetoslavganov <svetoslavganov@google.com> | 2009-05-14 22:28:01 -0700 |
---|---|---|
committer | svetoslavganov <svetoslavganov@google.com> | 2009-05-14 23:47:05 -0700 |
commit | 75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec (patch) | |
tree | 84e1843368037d24f83749d152f818d537267bfa /core/java/android/widget | |
parent | 669ec3a6e47248fee0a3a0f4877b46875eb42140 (diff) | |
download | frameworks_base-75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec.zip frameworks_base-75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec.tar.gz frameworks_base-75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec.tar.bz2 |
Accessibility feature - framework changes (replacing 698, 699, 700, 701 and merging with the latest Donut)
Diffstat (limited to 'core/java/android/widget')
-rw-r--r-- | core/java/android/widget/AdapterView.java | 43 | ||||
-rw-r--r-- | core/java/android/widget/CheckedTextView.java | 15 | ||||
-rw-r--r-- | core/java/android/widget/CompoundButton.java | 22 | ||||
-rw-r--r-- | core/java/android/widget/ImageView.java | 6 | ||||
-rw-r--r-- | core/java/android/widget/ListView.java | 27 | ||||
-rw-r--r-- | core/java/android/widget/PopupWindow.java | 16 | ||||
-rw-r--r-- | core/java/android/widget/RelativeLayout.java | 66 | ||||
-rw-r--r-- | core/java/android/widget/SlidingDrawer.java | 26 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 74 | ||||
-rw-r--r-- | core/java/android/widget/Toast.java | 5 |
10 files changed, 261 insertions, 39 deletions
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 173e80f..7d2fcbc 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -24,11 +24,12 @@ import android.os.SystemClock; import android.util.AttributeSet; import android.util.SparseArray; import android.view.ContextMenu; +import android.view.SoundEffectConstants; import android.view.View; -import android.view.ViewGroup; import android.view.ViewDebug; -import android.view.SoundEffectConstants; +import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; +import android.view.accessibility.AccessibilityEvent; /** @@ -618,7 +619,9 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } /** - * Sets the currently selected item + * Sets the currently selected item. To support accessibility subclasses that + * override this method must invoke the overriden super method first. + * * @param position Index (starting at 0) of the data item to be selected. */ public abstract void setSelection(int position); @@ -844,6 +847,11 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { fireOnSelected(); } } + + // we fire selection events here not in View + if (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED); + } } private void fireOnSelected() { @@ -861,6 +869,35 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = false; + // This is an exceptional case which occurs when a window gets the + // focus and sends a focus event via its focused child to announce + // current focus/selection. AdapterView fires selection but not focus + // events so we change the event type here. + if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) { + event.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED); + } + + // we send selection events only from AdapterView to avoid + // generation of such event for each child + View selectedView = getSelectedView(); + if (selectedView != null) { + populated = selectedView.dispatchPopulateAccessibilityEvent(event); + } + + if (!populated) { + if (selectedView != null) { + event.setEnabled(selectedView.isEnabled()); + } + event.setItemCount(getCount()); + event.setCurrentItemIndex(getSelectedItemPosition()); + } + + return populated; + } + + @Override protected boolean canAnimate() { return super.canAnimate() && mItemCount > 0; } diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index abcc715..fd590ed 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -16,14 +16,15 @@ package android.widget; +import com.android.internal.R; + import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.Gravity; - -import com.android.internal.R; +import android.view.accessibility.AccessibilityEvent; /** @@ -194,5 +195,13 @@ public class CheckedTextView extends TextView implements Checkable { invalidate(); } } - + + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + if (!populated) { + event.setChecked(mChecked); + } + return populated; + } } diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java index d4482dc..98b0976 100644 --- a/core/java/android/widget/CompoundButton.java +++ b/core/java/android/widget/CompoundButton.java @@ -26,7 +26,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.view.Gravity; - +import android.view.accessibility.AccessibilityEvent; /** * <p> @@ -124,6 +124,7 @@ public abstract class CompoundButton extends Button implements Checkable { if (mOnCheckedChangeWidgetListener != null) { mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); } + mBroadcasting = false; } } @@ -205,6 +206,25 @@ public abstract class CompoundButton extends Button implements Checkable { } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + + if (!populated) { + int resourceId = 0; + if (mChecked) { + resourceId = R.string.accessibility_compound_button_selected; + } else { + resourceId = R.string.accessibility_compound_button_unselected; + } + String state = getResources().getString(resourceId); + event.getText().add(state); + event.setChecked(mChecked); + } + + return populated; + } + + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 480b0b8..2796774 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -32,6 +32,8 @@ import android.net.Uri; import android.util.AttributeSet; import android.util.Log; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.widget.RemoteViews.RemoteView; @@ -848,7 +850,7 @@ public class ImageView extends View { public int getBaseline() { return mBaselineAligned ? getMeasuredHeight() : -1; } - + /** * Set a tinting option for the image. * @@ -878,7 +880,7 @@ public class ImageView extends View { invalidate(); } } - + public void setAlpha(int alpha) { alpha &= 0xFF; // keep it legal if (mAlpha != alpha) { diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 5472d68..c21c7fa 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -35,6 +35,7 @@ import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ViewParent; import android.view.SoundEffectConstants; +import android.view.accessibility.AccessibilityEvent; import com.google.android.collect.Lists; import com.android.internal.R; @@ -1845,6 +1846,32 @@ public class ListView extends AbsListView { } } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean populated = super.dispatchPopulateAccessibilityEvent(event); + + if (!populated) { + int itemCount = 0; + int currentItemIndex = getSelectedItemPosition(); + + ListAdapter adapter = getAdapter(); + if (adapter != null) { + for (int i = 0, count = adapter.getCount(); i < count; i++) { + if (adapter.isEnabled(i)) { + itemCount++; + } else if (i <= currentItemIndex) { + currentItemIndex--; + } + } + } + + event.setItemCount(itemCount); + event.setCurrentItemIndex(currentItemIndex); + } + + return populated; + } + /** * setSelectionAfterHeaderView set the selection to be the first list item * after the header views. diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 78c7bd8..975277b 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -18,6 +18,8 @@ package android.widget; import com.android.internal.R; +import android.content.Context; +import android.content.res.TypedArray; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; @@ -33,8 +35,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.graphics.drawable.StateListDrawable; import android.os.IBinder; -import android.content.Context; -import android.content.res.TypedArray; import android.util.AttributeSet; import java.lang.ref.WeakReference; @@ -1017,6 +1017,7 @@ public class PopupWindow { unregisterForScrollChanged(); mWindowManager.removeView(mPopupView); + if (mPopupView != mContentView && mPopupView instanceof ViewGroup) { ((ViewGroup) mPopupView).removeView(mContentView); } @@ -1316,7 +1317,16 @@ public class PopupWindow { return super.onTouchEvent(event); } } - + + @Override + public void sendAccessibilityEvent(int eventType) { + // clinets are interested in the content not the container, make it event source + if (mContentView != null) { + mContentView.sendAccessibilityEvent(eventType); + } else { + super.sendAccessibilityEvent(eventType); + } + } } } diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index edbb3db..ef240e0 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -16,17 +16,22 @@ package android.widget; +import com.android.internal.R; + import android.content.Context; import android.content.res.TypedArray; +import android.graphics.Rect; import android.util.AttributeSet; -import android.view.View; -import android.view.ViewGroup; import android.view.Gravity; +import android.view.View; import android.view.ViewDebug; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; -import android.graphics.Rect; -import com.android.internal.R; +import java.util.Comparator; +import java.util.SortedSet; +import java.util.TreeSet; /** * A Layout where the positions of the children can be described in relation to each other or to the @@ -137,6 +142,8 @@ public class RelativeLayout extends ViewGroup { private final Rect mSelfBounds = new Rect(); private int mIgnoreGravity; + private static SortedSet<View> mTopToBottomLeftToRightSet = null; + public RelativeLayout(Context context) { super(context); } @@ -782,6 +789,57 @@ public class RelativeLayout extends ViewGroup { return new LayoutParams(p); } + @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (mTopToBottomLeftToRightSet == null) { + mTopToBottomLeftToRightSet = new TreeSet<View>(new TopToBottomLeftToRightComparator()); + } + + // sort children top-to-bottom and left-to-right + for (int i = 0, count = getChildCount(); i < count; i++) { + mTopToBottomLeftToRightSet.add(getChildAt(i)); + } + + for (View view : mTopToBottomLeftToRightSet) { + if (view.dispatchPopulateAccessibilityEvent(event)) { + mTopToBottomLeftToRightSet.clear(); + return true; + } + } + + mTopToBottomLeftToRightSet.clear(); + return false; + } + + /** + * Compares two views in left-to-right and top-to-bottom fashion. + */ + private class TopToBottomLeftToRightComparator implements Comparator<View> { + public int compare(View first, View second) { + // top - bottom + int topDifference = first.getTop() - second.getTop(); + if (topDifference != 0) { + return topDifference; + } + // left - right + int leftDifference = first.getLeft() - second.getLeft(); + if (leftDifference != 0) { + return leftDifference; + } + // break tie by height + int heightDiference = first.getHeight() - second.getHeight(); + if (heightDiference != 0) { + return heightDiference; + } + // break tie by width + int widthDiference = first.getWidth() - second.getWidth(); + if (widthDiference != 0) { + return widthDiference; + } + return 0; + } + } + /** * Per-child layout information associated with RelativeLayout. * diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java index 92561ed..f706744 100644 --- a/core/java/android/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -16,21 +16,22 @@ package android.widget; -import android.view.ViewGroup; -import android.view.View; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.SoundEffectConstants; +import android.R; import android.content.Context; import android.content.res.TypedArray; -import android.util.AttributeSet; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.Bitmap; -import android.os.SystemClock; import android.os.Handler; import android.os.Message; -import android.R; +import android.os.SystemClock; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.VelocityTracker; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; /** * SlidingDrawer hides content out of the screen and allows the user to drag a handle @@ -746,6 +747,8 @@ public class SlidingDrawer extends ViewGroup { openDrawer(); invalidate(); requestLayout(); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); } /** @@ -777,6 +780,7 @@ public class SlidingDrawer extends ViewGroup { scrollListener.onScrollStarted(); } animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft()); + if (scrollListener != null) { scrollListener.onScrollEnded(); } @@ -798,6 +802,9 @@ public class SlidingDrawer extends ViewGroup { scrollListener.onScrollStarted(); } animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft()); + + sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + if (scrollListener != null) { scrollListener.onScrollEnded(); } @@ -827,6 +834,7 @@ public class SlidingDrawer extends ViewGroup { } mExpanded = true; + if (mOnDrawerOpenListener != null) { mOnDrawerOpenListener.onDrawerOpened(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index adfc74f..219afec 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -16,6 +16,11 @@ package android.widget; +import com.android.internal.util.FastMath; +import com.android.internal.widget.EditableInputConnection; + +import org.xmlpull.v1.XmlPullParserException; + import android.content.Context; import android.content.Intent; import android.content.res.ColorStateList; @@ -31,17 +36,17 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Handler; +import android.os.Message; import android.os.Parcel; import android.os.Parcelable; import android.os.ResultReceiver; import android.os.SystemClock; -import android.os.Message; import android.text.BoringLayout; +import android.text.ClipboardManager; import android.text.DynamicLayout; import android.text.Editable; import android.text.GetChars; import android.text.GraphicsOperations; -import android.text.ClipboardManager; import android.text.InputFilter; import android.text.InputType; import android.text.Layout; @@ -49,9 +54,9 @@ import android.text.ParcelableSpan; import android.text.Selection; import android.text.SpanWatcher; import android.text.Spannable; +import android.text.SpannableString; import android.text.Spanned; import android.text.SpannedString; -import android.text.SpannableString; import android.text.StaticLayout; import android.text.TextPaint; import android.text.TextUtils; @@ -64,19 +69,18 @@ import android.text.method.KeyListener; import android.text.method.LinkMovementMethod; import android.text.method.MetaKeyKeyListener; import android.text.method.MovementMethod; -import android.text.method.TimeKeyListener; - import android.text.method.PasswordTransformationMethod; import android.text.method.SingleLineTransformationMethod; import android.text.method.TextKeyListener; +import android.text.method.TimeKeyListener; import android.text.method.TransformationMethod; import android.text.style.ParagraphStyle; import android.text.style.URLSpan; import android.text.style.UpdateAppearance; import android.text.util.Linkify; import android.util.AttributeSet; -import android.util.Log; import android.util.FloatMath; +import android.util.Log; import android.util.TypedValue; import android.view.ContextMenu; import android.view.Gravity; @@ -89,25 +93,22 @@ import android.view.ViewDebug; import android.view.ViewRoot; import android.view.ViewTreeObserver; import android.view.ViewGroup.LayoutParams; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AnimationUtils; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; +import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.EditorInfo; import android.widget.RemoteViews.RemoteView; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; -import com.android.internal.util.FastMath; -import com.android.internal.widget.EditableInputConnection; - -import org.xmlpull.v1.XmlPullParserException; - /** * Displays text to the user and optionally allows them to edit it. A TextView * is a complete text editor, however the basic class is configured to not @@ -6129,10 +6130,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private class ChangeWatcher implements TextWatcher, SpanWatcher { + + private CharSequence mBeforeText; + public void beforeTextChanged(CharSequence buffer, int start, int before, int after) { if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start + " before=" + before + " after=" + after + ": " + buffer); + + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + mBeforeText = buffer.toString(); + } + TextView.this.sendBeforeTextChanged(buffer, start, before, after); } @@ -6141,6 +6150,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (DEBUG_EXTRACT) Log.v(TAG, "onTextChanged start=" + start + " before=" + before + " after=" + after + ": " + buffer); TextView.this.handleTextChanged(buffer, start, before, after); + + if (AccessibilityManager.getInstance(mContext).isEnabled() && + (isFocused() || isSelected() && + isShown())) { + sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); + mBeforeText = null; + } } public void afterTextChanged(Editable buffer) { @@ -6776,6 +6792,40 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } @Override + public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + boolean isPassword = + (mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) == + (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + + if (!isPassword) { + CharSequence text = getText(); + if (TextUtils.isEmpty(text)) { + text = getHint(); + } + if (!TextUtils.isEmpty(text)) { + if (text.length() > AccessibilityEvent.MAX_TEXT_LENGTH) { + text = text.subSequence(0, AccessibilityEvent.MAX_TEXT_LENGTH + 1); + } + event.getText().add(text); + } + } else { + event.setPassword(isPassword); + } + return false; + } + + void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, + int fromIndex, int removedCount, int addedCount) { + AccessibilityEvent event = + AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); + event.setFromIndex(fromIndex); + event.setRemovedCount(removedCount); + event.setAddedCount(addedCount); + event.setBeforeText(beforeText); + sendAccessibilityEventUnchecked(event); + } + + @Override protected void onCreateContextMenu(ContextMenu menu) { super.onCreateContextMenu(menu); boolean added = false; diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java index ff74787..670692f 100644 --- a/core/java/android/widget/Toast.java +++ b/core/java/android/widget/Toast.java @@ -21,8 +21,8 @@ import android.app.ITransientNotification; import android.content.Context; import android.content.res.Resources; import android.graphics.PixelFormat; -import android.os.RemoteException; import android.os.Handler; +import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; import android.view.Gravity; @@ -278,7 +278,7 @@ public class Toast { } tv.setText(s); } - + // ======================================================================================= // All the gunk below is the interaction with the Notification Service, which handles // the proper ordering of these system-wide. @@ -373,6 +373,7 @@ public class Toast { TAG, "REMOVE! " + mView + " in " + this); mWM.removeView(mView); } + mView = null; } } |