summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget
diff options
context:
space:
mode:
authorsvetoslavganov <svetoslavganov@google.com>2009-05-14 22:28:01 -0700
committersvetoslavganov <svetoslavganov@google.com>2009-05-14 23:47:05 -0700
commit75986cf9bc57ef11ad70f36fb77fbbf5d63af6ec (patch)
tree84e1843368037d24f83749d152f818d537267bfa /core/java/android/widget
parent669ec3a6e47248fee0a3a0f4877b46875eb42140 (diff)
downloadframeworks_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.java43
-rw-r--r--core/java/android/widget/CheckedTextView.java15
-rw-r--r--core/java/android/widget/CompoundButton.java22
-rw-r--r--core/java/android/widget/ImageView.java6
-rw-r--r--core/java/android/widget/ListView.java27
-rw-r--r--core/java/android/widget/PopupWindow.java16
-rw-r--r--core/java/android/widget/RelativeLayout.java66
-rw-r--r--core/java/android/widget/SlidingDrawer.java26
-rw-r--r--core/java/android/widget/TextView.java74
-rw-r--r--core/java/android/widget/Toast.java5
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;
}
}