Accessibility feature - framework changes (replacing 698, 699, 700, 701 and merging with the latest Donut)
diff --git a/Android.mk b/Android.mk
index ca76fb4..bbda5fb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -64,6 +64,8 @@
##
## READ ME: ########################################################
LOCAL_SRC_FILES += \
+ core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl \
+ core/java/android/accessibilityservice/IEventListener.aidl \
core/java/android/accounts/IAccountsService.aidl \
core/java/android/app/IActivityPendingResult.aidl \
core/java/android/app/IActivityWatcher.aidl \
@@ -106,6 +108,8 @@
core/java/android/os/IPermissionController.aidl \
core/java/android/os/IPowerManager.aidl \
core/java/android/text/IClipboard.aidl \
+ core/java/android/view/accessibility/IAccessibilityManager.aidl \
+ core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
core/java/android/view/IApplicationToken.aidl \
core/java/android/view/IOnKeyguardExitResult.aidl \
core/java/android/view/IRotationWatcher.aidl \
diff --git a/api/current.xml b/api/current.xml
index eaf1512..c03526d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -2550,6 +2550,17 @@
visibility="public"
>
</field>
+<field name="contentDescription"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843379"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="cropToPadding"
type="int"
transient="false"
@@ -12656,6 +12667,270 @@
</constructor>
</class>
</package>
+<package name="android.accessibilityservice"
+>
+<class name="AccessibilityService"
+ extends="android.app.Service"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="AccessibilityService"
+ type="android.accessibilityservice.AccessibilityService"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onAccessibilityEvent"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
+<method name="onBind"
+ return="android.os.IBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="intent" type="android.content.Intent">
+</parameter>
+</method>
+<method name="onInterrupt"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="onServiceConnected"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="protected"
+>
+</method>
+<method name="setServiceInfo"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="info" type="android.accessibilityservice.AccessibilityServiceInfo">
+</parameter>
+</method>
+<field name="SERVICE_INTERFACE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""android.accessibilityservice.AccessibilityService""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="AccessibilityServiceInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<constructor name="AccessibilityServiceInfo"
+ type="android.accessibilityservice.AccessibilityServiceInfo"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="DEFAULT"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEEDBACK_AUDIBLE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEEDBACK_GENERIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEEDBACK_HAPTIC"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEEDBACK_SPOKEN"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FEEDBACK_VISUAL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="eventTypes"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="feedbackType"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="notificationTimeout"
+ type="long"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="packageNames"
+ type="java.lang.String[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+</package>
<package name="android.app"
>
<class name="Activity"
@@ -12764,6 +13039,19 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
+<method name="dispatchPopulateAccessibilityEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
<method name="dispatchTouchEvent"
return="boolean"
abstract="false"
@@ -16759,6 +17047,19 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
+<method name="dispatchPopulateAccessibilityEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
<method name="dispatchTouchEvent"
return="boolean"
abstract="false"
@@ -24974,6 +25275,17 @@
<parameter name="receiver" type="android.content.BroadcastReceiver">
</parameter>
</method>
+<field name="ACCESSIBILITY_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""accessibility""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ACTIVITY_SERVICE"
type="java.lang.String"
transient="false"
@@ -101868,6 +102180,17 @@
<parameter name="value" type="java.lang.String">
</parameter>
</method>
+<field name="ACCESSIBILITY_ENABLED"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""accessibility_enabled""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ADB_ENABLED"
type="java.lang.String"
transient="false"
@@ -101966,6 +102289,17 @@
visibility="public"
>
</field>
+<field name="ENABLED_ACCESSIBILITY_SERVICES"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""enabled_accessibility_services""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="ENABLED_INPUT_METHODS"
type="java.lang.String"
transient="false"
@@ -134575,6 +134909,8 @@
deprecated="not deprecated"
visibility="public"
>
+<implements name="android.view.accessibility.AccessibilityEventSource">
+</implements>
<implements name="android.graphics.drawable.Drawable.Callback">
</implements>
<implements name="android.view.KeyEvent.Callback">
@@ -134630,6 +134966,23 @@
<parameter name="direction" type="int">
</parameter>
</method>
+<method name="addFocusables"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="views" type="java.util.ArrayList<android.view.View>">
+</parameter>
+<parameter name="direction" type="int">
+</parameter>
+<parameter name="focusableMode" type="int">
+</parameter>
+</method>
<method name="addTouchables"
return="void"
abstract="false"
@@ -134864,6 +135217,19 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
+<method name="dispatchPopulateAccessibilityEvent"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
<method name="dispatchRestoreInstanceState"
return="void"
abstract="false"
@@ -135158,6 +135524,17 @@
visibility="protected"
>
</method>
+<method name="getContentDescription"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
<method name="getContext"
return="android.content.Context"
abstract="false"
@@ -137114,6 +137491,32 @@
<parameter name="y" type="int">
</parameter>
</method>
+<method name="sendAccessibilityEvent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventType" type="int">
+</parameter>
+</method>
+<method name="sendAccessibilityEventUnchecked"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
<method name="setAnimation"
return="void"
abstract="false"
@@ -137179,6 +137582,19 @@
<parameter name="clickable" type="boolean">
</parameter>
</method>
+<method name="setContentDescription"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="contentDescription" type="java.lang.CharSequence">
+</parameter>
+</method>
<method name="setDrawingCacheBackgroundColor"
return="void"
abstract="false"
@@ -137967,6 +138383,28 @@
visibility="protected"
>
</field>
+<field name="FOCUSABLES_ALL"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="0"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="FOCUSABLES_TOUCH_MODE"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="FOCUSED_SELECTED_STATE_SET"
type="int[]"
transient="false"
@@ -142659,6 +143097,19 @@
<parameter name="event" type="android.view.KeyEvent">
</parameter>
</method>
+<method name="dispatchPopulateAccessibilityEvent"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
<method name="dispatchTouchEvent"
return="boolean"
abstract="true"
@@ -144083,6 +144534,698 @@
</field>
</class>
</package>
+<package name="android.view.accessibility"
+>
+<class name="AccessibilityEvent"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getAddedCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getBeforeText"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getClassName"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getContentDescription"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getCurrentItemIndex"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEventTime"
+ return="long"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getEventType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getFromIndex"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getItemCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getPackageName"
+ return="java.lang.CharSequence"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getParcelableData"
+ return="android.os.Parcelable"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getRemovedCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getText"
+ return="java.util.List<java.lang.CharSequence>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="initFromParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+</method>
+<method name="isChecked"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isFullScreen"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isPassword"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="obtain"
+ return="android.view.accessibility.AccessibilityEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventType" type="int">
+</parameter>
+</method>
+<method name="obtain"
+ return="android.view.accessibility.AccessibilityEvent"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="recycle"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setAddedCount"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="addedCount" type="int">
+</parameter>
+</method>
+<method name="setBeforeText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="beforeText" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setChecked"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isChecked" type="boolean">
+</parameter>
+</method>
+<method name="setClassName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="className" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setContentDescription"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="contentDescription" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setCurrentItemIndex"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="currentItemIndex" type="int">
+</parameter>
+</method>
+<method name="setEnabled"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isEnabled" type="boolean">
+</parameter>
+</method>
+<method name="setEventTime"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventTime" type="long">
+</parameter>
+</method>
+<method name="setEventType"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventType" type="int">
+</parameter>
+</method>
+<method name="setFromIndex"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="fromIndex" type="int">
+</parameter>
+</method>
+<method name="setFullScreen"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isFullScreen" type="boolean">
+</parameter>
+</method>
+<method name="setItemCount"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="itemCount" type="int">
+</parameter>
+</method>
+<method name="setPackageName"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="packageName" type="java.lang.CharSequence">
+</parameter>
+</method>
+<method name="setParcelableData"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcelableData" type="android.os.Parcelable">
+</parameter>
+</method>
+<method name="setPassword"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="isPassword" type="boolean">
+</parameter>
+</method>
+<method name="setRemovedCount"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="removedCount" type="int">
+</parameter>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="parcel" type="android.os.Parcel">
+</parameter>
+<parameter name="flags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="INVALID_POSITION"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="MAX_TEXT_LENGTH"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="500"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPES_ALL_MASK"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="-1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_NOTIFICATION_STATE_CHANGED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="64"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_VIEW_CLICKED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_VIEW_FOCUSED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="8"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_VIEW_LONG_CLICKED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="2"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_VIEW_SELECTED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="4"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_VIEW_TEXT_CHANGED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="TYPE_WINDOW_STATE_CHANGED"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="32"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<interface name="AccessibilityEventSource"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="sendAccessibilityEvent"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="eventType" type="int">
+</parameter>
+</method>
+<method name="sendAccessibilityEventUnchecked"
+ return="void"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
+</interface>
+<class name="AccessibilityManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getAccessibilityServiceList"
+ return="java.util.List<android.content.pm.ServiceInfo>"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="interrupt"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="isEnabled"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="sendAccessibilityEvent"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="event" type="android.view.accessibility.AccessibilityEvent">
+</parameter>
+</method>
+</class>
+</package>
<package name="android.view.animation"
>
<class name="AccelerateDecelerateInterpolator"
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
new file mode 100644
index 0000000..a3456c7
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2009 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 android.accessibilityservice;
+
+import com.android.internal.os.HandlerCaller;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * An accessibility service runs in the background and receives callbacks by the system
+ * when {@link AccessibilityEvent}s are fired. Such events denote some state transition
+ * in the user interface, for example, the focus has changed, a button has been clicked,
+ * etc.
+ * <p>
+ * An accessibility service extends this class and implements its abstract methods. Such
+ * a service is declared as any other service in an AndroidManifest.xml but it must also
+ * specify that it handles the "android.accessibilityservice.AccessibilityService"
+ * {@link android.content.Intent}. Following is an example of such a declaration:
+ * <p>
+ * <code>
+ * <service android:name=".MyAccessibilityService"><br>
+ * <intent-filter><br>
+ * <action android:name="android.accessibilityservice.AccessibilityService" /><br>
+ * </intent-filter><br>
+ * </service><br>
+ * </code>
+ * <p>
+ * The lifecycle of an accessibility service is managed exclusively by the system. Starting
+ * or stopping an accessibility service is triggered by an explicit user action through
+ * enabling or disabling it in the device settings. After the system binds to a service it
+ * calls {@link AccessibilityService#onServiceConnected()}. This method can be
+ * overriden by clients that want to perform post binding setup. An accessibility service
+ * is configured though setting an {@link AccessibilityServiceInfo} by calling
+ * {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. You can call this
+ * method any time to change the service configuration but it is good practice to do that
+ * in the overriden {@link AccessibilityService#onServiceConnected()}.
+ * <p>
+ * An accessibility service can be registered for events in specific packages to provide a
+ * specific type of feedback and is notified with a certain timeout after the last event
+ * of interest has been fired.
+ * <p>
+ * <b>Notification strategy</b>
+ * <p>
+ * For each feedback type only one accessibility service is notified. Services are notified
+ * in the order of registration. Hence, if two services are registered for the same
+ * feedback type in the same package the first one wins. It is possible however, to
+ * register a service as the default one for a given feedback type. In such a case this
+ * service is invoked if no other service was interested in the event. In other words, default
+ * services do not compete with other services and are notified last regardless of the
+ * registration order. This enables "generic" accessibility services that work reasonably
+ * well with most applications to coexist with "polished" ones that are targeted for
+ * specific applications.
+ * <p>
+ * <b>Event types</b>
+ * <p>
+ * {@link AccessibilityEvent#TYPE_VIEW_CLICKED}
+ * {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED}
+ * {@link AccessibilityEvent#TYPE_VIEW_FOCUSED}
+ * {@link AccessibilityEvent#TYPE_VIEW_SELECTED}
+ * {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED}
+ * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}
+ * {@link AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED}
+ * <p>
+ * <b>Feedback types</b>
+ * <p>
+ * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
+ * {@link AccessibilityServiceInfo#FEEDBACK_HAPTIC}
+ * {@link AccessibilityServiceInfo#FEEDBACK_AUDIBLE}
+ * {@link AccessibilityServiceInfo#FEEDBACK_VISUAL}
+ * {@link AccessibilityServiceInfo#FEEDBACK_GENERIC}
+ *
+ * @see AccessibilityEvent
+ * @see AccessibilityServiceInfo
+ * @see android.view.accessibility.AccessibilityManager
+ *
+ * Note: The event notification timeout is useful to avoid propagating events to the client
+ * too frequently since this is accomplished via an expensive interprocess call.
+ * One can think of the timeout as a criteria to determine when event generation has
+ * settled down.
+ */
+public abstract class AccessibilityService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.accessibilityservice.AccessibilityService";
+
+ private static final String LOG_TAG = "AccessibilityService";
+
+ private AccessibilityServiceInfo mInfo;
+
+ IAccessibilityServiceConnection mConnection;
+
+ /**
+ * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
+ *
+ * @param event An event.
+ */
+ public abstract void onAccessibilityEvent(AccessibilityEvent event);
+
+ /**
+ * Callback for interrupting the accessibility feedback.
+ */
+ public abstract void onInterrupt();
+
+ /**
+ * This method is a part of the {@link AccessibilityService} lifecycle and is
+ * called after the system has successfully bound to the service. If is
+ * convenient to use this method for setting the {@link AccessibilityServiceInfo}.
+ *
+ * @see AccessibilityServiceInfo
+ * @see #setServiceInfo(AccessibilityServiceInfo)
+ */
+ protected void onServiceConnected() {
+
+ }
+
+ /**
+ * Sets the {@link AccessibilityServiceInfo} that describes this service.
+ * <p>
+ * Note: You can call this method any time but the info will be picked up after
+ * the system has bound to this service and when this method is called thereafter.
+ *
+ * @param info The info.
+ */
+ public final void setServiceInfo(AccessibilityServiceInfo info) {
+ mInfo = info;
+ sendServiceInfo();
+ }
+
+ /**
+ * Sets the {@link AccessibilityServiceInfo} for this service if the latter is
+ * properly set and there is an {@link IAccessibilityServiceConnection} to the
+ * AccessibilityManagerService.
+ */
+ private void sendServiceInfo() {
+ if (mInfo != null && mConnection != null) {
+ try {
+ mConnection.setServiceInfo(mInfo);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re);
+ }
+ }
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return new IEventListenerWrapper(this);
+ }
+
+ /**
+ * Implements the internal {@link IEventListener} interface to convert
+ * incoming calls to it back to calls on an {@link AccessibilityService}.
+ */
+ class IEventListenerWrapper extends IEventListener.Stub
+ implements HandlerCaller.Callback {
+
+ private static final int DO_SET_SET_CONNECTION = 10;
+ private static final int DO_ON_INTERRUPT = 20;
+ private static final int DO_ON_ACCESSIBILITY_EVENT = 30;
+
+ private final HandlerCaller mCaller;
+
+ private AccessibilityService mTarget;
+
+ public IEventListenerWrapper(AccessibilityService context) {
+ mTarget = context;
+ mCaller = new HandlerCaller(context, this);
+ }
+
+ public void setConnection(IAccessibilityServiceConnection connection) {
+ Message message = mCaller.obtainMessageO(DO_SET_SET_CONNECTION, connection);
+ mCaller.sendMessage(message);
+ }
+
+ public void onInterrupt() {
+ Message message = mCaller.obtainMessage(DO_ON_INTERRUPT);
+ mCaller.sendMessage(message);
+ }
+
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ Message message = mCaller.obtainMessageO(DO_ON_ACCESSIBILITY_EVENT, event);
+ mCaller.sendMessage(message);
+ }
+
+ public void executeMessage(Message message) {
+ switch (message.what) {
+ case DO_ON_ACCESSIBILITY_EVENT :
+ AccessibilityEvent event = (AccessibilityEvent) message.obj;
+ mTarget.onAccessibilityEvent(event);
+ event.recycle();
+ return;
+ case DO_ON_INTERRUPT :
+ mTarget.onInterrupt();
+ return;
+ case DO_SET_SET_CONNECTION :
+ mConnection = ((IAccessibilityServiceConnection) message.obj);
+ mTarget.onServiceConnected();
+ return;
+ default :
+ Log.w(LOG_TAG, "Unknown message type " + message.what);
+ }
+ }
+ }
+}
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl b/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl
new file mode 100644
index 0000000..1f5d385
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 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 android.accessibilityservice;
+
+parcelable AccessibilityServiceInfo;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
new file mode 100644
index 0000000..4761f98
--- /dev/null
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2009 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 android.accessibilityservice;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class describes an {@link AccessibilityService}. The system
+ * notifies an {@link AccessibilityService} for
+ * {@link android.view.accessibility.AccessibilityEvent}s
+ * according to the information encapsulated in this class.
+ *
+ * @see AccessibilityService
+ * @see android.view.accessibility.AccessibilityEvent
+ */
+public class AccessibilityServiceInfo implements Parcelable {
+
+ /**
+ * Denotes spoken feedback.
+ */
+ public static final int FEEDBACK_SPOKEN = 0x0000001;
+
+ /**
+ * Denotes haptic feedback.
+ */
+ public static final int FEEDBACK_HAPTIC = 0x0000002;
+
+ /**
+ * Denotes audible (not spoken) feedback.
+ */
+ public static final int FEEDBACK_AUDIBLE = 0x0000004;
+
+ /**
+ * Denotes visual feedback.
+ */
+ public static final int FEEDBACK_VISUAL = 0x0000008;
+
+ /**
+ * Denotes generic feedback.
+ */
+ public static final int FEEDBACK_GENERIC = 0x0000010;
+
+ /**
+ * If an {@link AccessibilityService} is the default for a given type.
+ * Default service is invoked only if no package specific one exists. In case of
+ * more than one package specific service only the earlier registered is notified.
+ */
+ public static final int DEFAULT = 0x0000001;
+
+ /**
+ * The event types an {@link AccessibilityService} is interested in.
+ *
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_ACTIVITY_STARTED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
+ * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED
+ */
+ public int eventTypes;
+
+ /**
+ * The package names an {@link AccessibilityService} is interested in. Setting
+ * to null is equivalent to all packages.
+ */
+ public String[] packageNames;
+
+ /**
+ * The feedback type an {@link AccessibilityService} provides.
+ *
+ * @see #FEEDBACK_AUDIBLE
+ * @see #FEEDBACK_GENERIC
+ * @see #FEEDBACK_HAPTIC
+ * @see #FEEDBACK_SPOKEN
+ * @see #FEEDBACK_VISUAL
+ */
+ public int feedbackType;
+
+ /**
+ * The timeout after the most recent event of a given type before an
+ * {@link AccessibilityService} is notified.
+ * <p>
+ * Note: The event notification timeout is useful to avoid propagating events to the client
+ * too frequently since this is accomplished via an expensive interprocess call.
+ * One can think of the timeout as a criteria to determine when event generation has
+ * settled down
+ */
+ public long notificationTimeout;
+
+ /**
+ * This field represents a set of flags used for configuring an
+ * {@link AccessibilityService}.
+ *
+ * @see #DEFAULT
+ */
+ public int flags;
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(eventTypes);
+ parcel.writeStringArray(packageNames);
+ parcel.writeInt(feedbackType);
+ parcel.writeLong(notificationTimeout);
+ parcel.writeInt(flags);
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR =
+ new Parcelable.Creator<AccessibilityServiceInfo>() {
+ public AccessibilityServiceInfo createFromParcel(Parcel parcel) {
+ AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ info.eventTypes = parcel.readInt();
+ info.packageNames = parcel.readStringArray();
+ info.feedbackType = parcel.readInt();
+ info.notificationTimeout = parcel.readLong();
+ info.flags = parcel.readInt();
+ return info;
+ }
+
+ public AccessibilityServiceInfo[] newArray(int size) {
+ return new AccessibilityServiceInfo[size];
+ }
+ };
+}
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
new file mode 100644
index 0000000..7157def
--- /dev/null
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2009 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 android.accessibilityservice;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+
+/**
+ * Interface AccessibilityManagerService#Service implements, and passes to an
+ * AccessibilityService so it can dynamically configure how the system handles it.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityServiceConnection {
+
+ void setServiceInfo(in AccessibilityServiceInfo info);
+}
diff --git a/core/java/android/accessibilityservice/IEventListener.aidl b/core/java/android/accessibilityservice/IEventListener.aidl
new file mode 100644
index 0000000..5b849f1
--- /dev/null
+++ b/core/java/android/accessibilityservice/IEventListener.aidl
@@ -0,0 +1,34 @@
+/*
+** Copyright 2009, 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 android.accessibilityservice;
+
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.view.accessibility.AccessibilityEvent;
+
+/**
+ * Top-level interface to accessibility service component (implemented in Service).
+ *
+ * @hide
+ */
+ oneway interface IEventListener {
+
+ void setConnection(in IAccessibilityServiceConnection connection);
+
+ void onAccessibilityEvent(in AccessibilityEvent event);
+
+ void onInterrupt();
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 9b1f0f9..f9b3d05 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,8 @@
package android.app;
+import com.android.internal.policy.PolicyManager;
+
import android.content.ComponentCallbacks;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -32,11 +34,12 @@
import android.media.AudioManager;
import android.net.Uri;
import android.os.Bundle;
-import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
+import android.os.RemoteException;
import android.text.Selection;
import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.util.Config;
@@ -58,10 +61,10 @@
import android.view.WindowManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
+import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.AdapterView;
-import com.android.internal.policy.PolicyManager;
-
import java.util.ArrayList;
import java.util.HashMap;
@@ -2013,7 +2016,24 @@
}
return onTrackballEvent(ev);
}
-
+
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ event.setClassName(getClass().getName());
+ event.setPackageName(getPackageName());
+
+ LayoutParams params = getWindow().getAttributes();
+ boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) &&
+ (params.height == LayoutParams.FILL_PARENT);
+ event.setFullScreen(isFullScreen);
+
+ CharSequence title = getTitle();
+ if (!TextUtils.isEmpty(title)) {
+ event.getText().add(title);
+ }
+
+ return true;
+ }
+
/**
* Default implementation of
* {@link android.view.Window.Callback#onCreatePanelView}
diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java
index bb17dc3..81e894f 100644
--- a/core/java/android/app/ApplicationContext.java
+++ b/core/java/android/app/ApplicationContext.java
@@ -16,8 +16,11 @@
package android.app;
-import com.google.android.collect.Maps;
+import com.android.internal.policy.PolicyManager;
import com.android.internal.util.XmlUtils;
+import com.google.android.collect.Maps;
+
+import org.xmlpull.v1.XmlPullParserException;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.IBluetoothDevice;
@@ -37,9 +40,9 @@
import android.content.pm.ComponentInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
-import android.content.pm.IPackageStatsObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
+import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -68,15 +71,15 @@
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Bundle;
-import android.os.Looper;
-import android.os.RemoteException;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.os.Process;
+import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.Vibrator;
import android.os.FileUtils.FileStatus;
@@ -87,10 +90,9 @@
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.WindowManagerImpl;
+import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
-import com.android.internal.policy.PolicyManager;
-
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -100,16 +102,14 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.WeakHashMap;
import java.util.Set;
-import java.util.HashSet;
+import java.util.WeakHashMap;
import java.util.Map.Entry;
-import org.xmlpull.v1.XmlPullParserException;
-
class ReceiverRestrictedContext extends ContextWrapper {
ReceiverRestrictedContext(Context base) {
super(base);
@@ -172,6 +172,7 @@
private Resources.Theme mTheme = null;
private PackageManager mPackageManager;
private NotificationManager mNotificationManager = null;
+ private AccessibilityManager mAccessibilityManager = null;
private ActivityManager mActivityManager = null;
private Context mReceiverRestrictedContext = null;
private SearchManager mSearchManager = null;
@@ -904,6 +905,8 @@
return getNotificationManager();
} else if (KEYGUARD_SERVICE.equals(name)) {
return new KeyguardManager();
+ } else if (ACCESSIBILITY_SERVICE.equals(name)) {
+ return AccessibilityManager.getInstance(this);
} else if (LOCATION_SERVICE.equals(name)) {
return getLocationManager();
} else if (SEARCH_SERVICE.equals(name)) {
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index b09a57f..222fe75 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -16,32 +16,34 @@
package android.app;
+import com.android.internal.policy.PolicyManager;
+
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.Bundle;
import android.util.Config;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
-import android.view.LayoutInflater;
import android.view.Window;
import android.view.WindowManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnCreateContextMenuListener;
-
-import com.android.internal.policy.PolicyManager;
+import android.view.ViewGroup.LayoutParams;
+import android.view.accessibility.AccessibilityEvent;
import java.lang.ref.WeakReference;
@@ -81,6 +83,7 @@
* {@hide}
*/
protected boolean mCancelable = true;
+
private Message mCancelMessage;
private Message mDismissMessage;
@@ -209,7 +212,9 @@
if (mShowing) {
if (Config.LOGV) Log.v(LOG_TAG,
"[Dialog] start: already showing, ignore");
- if (mDecor != null) mDecor.setVisibility(View.VISIBLE);
+ if (mDecor != null) {
+ mDecor.setVisibility(View.VISIBLE);
+ }
return;
}
@@ -236,7 +241,9 @@
* Hide the dialog, but do not dismiss it.
*/
public void hide() {
- if (mDecor != null) mDecor.setVisibility(View.GONE);
+ if (mDecor != null) {
+ mDecor.setVisibility(View.GONE);
+ }
}
/**
@@ -266,6 +273,7 @@
}
mWindowManager.removeView(mDecor);
+
mDecor = null;
mWindow.closeAllPanels();
onStop();
@@ -280,7 +288,7 @@
Message.obtain(mDismissMessage).sendToTarget();
}
}
-
+
// internal method to make sure mcreated is set properly without requiring
// users to call through to super in onCreate
void dispatchOnCreate(Bundle savedInstanceState) {
@@ -608,6 +616,18 @@
return onTrackballEvent(ev);
}
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ event.setClassName(getClass().getName());
+ event.setPackageName(mContext.getPackageName());
+
+ LayoutParams params = getWindow().getAttributes();
+ boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) &&
+ (params.height == LayoutParams.FILL_PARENT);
+ event.setFullScreen(isFullScreen);
+
+ return false;
+ }
+
/**
* @see Activity#onCreatePanelView(int)
*/
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f2ad248..c328d1614 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1135,6 +1135,15 @@
public static final String NOTIFICATION_SERVICE = "notification";
/**
* Use with {@link #getSystemService} to retrieve a
+ * {@link android.view.accessibility.AccessibilityManager} for giving the user
+ * feedback for UI events through the registered event listeners.
+ *
+ * @see #getSystemService
+ * @see android.view.accessibility.AccessibilityManager
+ */
+ public static final String ACCESSIBILITY_SERVICE = "accessibility";
+ /**
+ * Use with {@link #getSystemService} to retrieve a
* {@link android.app.NotificationManager} for controlling keyguard.
*
* @see #getSystemService
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index 1e9b7ae..cf5664c 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -16,6 +16,7 @@
package android.preference;
+import android.app.Service;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
@@ -23,6 +24,8 @@
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Checkable;
import android.widget.TextView;
@@ -42,6 +45,9 @@
private CharSequence mSummaryOff;
private boolean mChecked;
+ private boolean mSendAccessibilityEventViewClickedType;
+
+ private AccessibilityManager mAccessibilityManager;
private boolean mDisableDependentsState;
@@ -55,6 +61,9 @@
mDisableDependentsState = a.getBoolean(
com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false);
a.recycle();
+
+ mAccessibilityManager =
+ (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
}
public CheckBoxPreference(Context context, AttributeSet attrs) {
@@ -64,14 +73,26 @@
public CheckBoxPreference(Context context) {
this(context, null);
}
-
+
@Override
protected void onBindView(View view) {
super.onBindView(view);
-
+
View checkboxView = view.findViewById(com.android.internal.R.id.checkbox);
if (checkboxView != null && checkboxView instanceof Checkable) {
((Checkable) checkboxView).setChecked(mChecked);
+
+ // send an event to announce the value change of the CheckBox and is done here
+ // because clicking a preference does not immediately change the checked state
+ // for example when enabling the WiFi
+ if (mSendAccessibilityEventViewClickedType &&
+ mAccessibilityManager.isEnabled() &&
+ checkboxView.isEnabled()) {
+ mSendAccessibilityEventViewClickedType = false;
+
+ int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED;
+ checkboxView.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
+ }
}
// Sync the summary view
@@ -85,7 +106,7 @@
summaryView.setText(mSummaryOff);
useDefaultSummary = false;
}
-
+
if (useDefaultSummary) {
final CharSequence summary = getSummary();
if (summary != null) {
@@ -111,6 +132,10 @@
boolean newValue = !isChecked();
+ // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change
+ // not sending
+ mSendAccessibilityEventViewClickedType = true;
+
if (!callChangeListener(newValue)) {
return;
}
@@ -124,10 +149,11 @@
* @param checked The checked state.
*/
public void setChecked(boolean checked) {
+
mChecked = checked;
persistBoolean(checked);
-
+
notifyDependencyChange(shouldDisableDependents());
notifyChanged();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4dd6524..14ef810 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1905,6 +1905,17 @@
public static final String USE_GOOGLE_MAIL = "use_google_mail";
/**
+ * If accessibility is enabled.
+ */
+ public static final String ACCESSIBILITY_ENABLED = "accessibility_enabled";
+
+ /**
+ * List of the enabled accessibility providers.
+ */
+ public static final String ENABLED_ACCESSIBILITY_SERVICES =
+ "enabled_accessibility_services";
+
+ /**
* Whether to notify the user of open networks.
* <p>
* If not connected and the scan results have an open network, we will
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 16b70ed..1564fd0 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,9 @@
package android.view;
+import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
+
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
@@ -25,12 +28,12 @@
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Shader;
-import android.graphics.Point;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
@@ -42,30 +45,30 @@
import android.os.SystemClock;
import android.os.SystemProperties;
import android.util.AttributeSet;
+import android.util.Config;
import android.util.EventLog;
import android.util.Log;
-import android.util.SparseArray;
-import android.util.Poolable;
import android.util.Pool;
-import android.util.Pools;
+import android.util.Poolable;
import android.util.PoolableManager;
-import android.util.Config;
+import android.util.Pools;
+import android.util.SparseArray;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityEventSource;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.Animation;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
-import android.view.inputmethod.EditorInfo;
import android.widget.ScrollBarDrawable;
-import com.android.internal.R;
-import com.android.internal.view.menu.MenuBuilder;
-
+import java.lang.ref.SoftReference;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.WeakHashMap;
-import java.lang.ref.SoftReference;
-import java.lang.reflect.Method;
-import java.lang.reflect.InvocationTargetException;
/**
* <p>
@@ -553,7 +556,7 @@
*
* @see android.view.ViewGroup
*/
-public class View implements Drawable.Callback, KeyEvent.Callback {
+public class View implements Drawable.Callback, KeyEvent.Callback, AccessibilityEventSource {
private static final boolean DBG = false;
/**
@@ -851,6 +854,18 @@
public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
/**
+ * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
+ * should add all focusable Views regardless if they are focusable in touch mode.
+ */
+ public static final int FOCUSABLES_ALL = 0x00000000;
+
+ /**
+ * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
+ * should add only Views focusable in touch mode.
+ */
+ public static final int FOCUSABLES_TOUCH_MODE = 0x00000001;
+
+ /**
* Use with {@link #focusSearch}. Move focus to the previous selectable
* item.
*/
@@ -1551,6 +1566,11 @@
protected int mPaddingBottom;
/**
+ * Briefly describes the view and is primarily used for accessibility support.
+ */
+ private CharSequence mContentDescription;
+
+ /**
* Cache the paddingRight set by the user to append to the scrollbar's size.
*/
@ViewDebug.ExportedProperty
@@ -1858,6 +1878,9 @@
viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK;
}
break;
+ case com.android.internal.R.styleable.View_contentDescription:
+ mContentDescription = a.getString(attr);
+ break;
case com.android.internal.R.styleable.View_soundEffectsEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
@@ -2255,6 +2278,8 @@
* otherwise is returned.
*/
public boolean performClick() {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
@@ -2272,6 +2297,8 @@
* otherwise is returned.
*/
public boolean performLongClick() {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
+
boolean handled = false;
if (mOnLongClickListener != null) {
handled = mOnLongClickListener.onLongClick(View.this);
@@ -2492,6 +2519,10 @@
* from (in addition to direction). Will be <code>null</code> otherwise.
*/
protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ if (gainFocus) {
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+
InputMethodManager imm = InputMethodManager.peekInstance();
if (!gainFocus) {
if (isPressed()) {
@@ -2514,6 +2545,79 @@
}
/**
+ * {@inheritDoc}
+ */
+ public void sendAccessibilityEvent(int eventType) {
+ if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+ sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void sendAccessibilityEventUnchecked(AccessibilityEvent event) {
+ event.setClassName(getClass().getName());
+ event.setPackageName(getContext().getPackageName());
+ event.setEnabled(isEnabled());
+ event.setContentDescription(mContentDescription);
+
+ if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
+ ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList;
+ getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL);
+ event.setItemCount(focusablesTempList.size());
+ event.setCurrentItemIndex(focusablesTempList.indexOf(this));
+ focusablesTempList.clear();
+ }
+
+ dispatchPopulateAccessibilityEvent(event);
+
+ AccessibilityManager.getInstance(mContext).sendAccessibilityEvent(event);
+ }
+
+ /**
+ * Dispatches an {@link AccessibilityEvent} to the {@link View} children
+ * to be populated.
+ *
+ * @param event The event.
+ *
+ * @return True if the event population was completed.
+ */
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ return false;
+ }
+
+ /**
+ * Gets the {@link View} description. It briefly describes the view and is
+ * primarily used for accessibility support. Set this property to enable
+ * better accessibility support for your application. This is especially
+ * true for views that do not have textual representation (For example,
+ * ImageButton).
+ *
+ * @return The content descriptiopn.
+ *
+ * @attr ref android.R.styleable#View_contentDescription
+ */
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Sets the {@link View} description. It briefly describes the view and is
+ * primarily used for accessibility support. Set this property to enable
+ * better accessibility support for your application. This is especially
+ * true for views that do not have textual representation (For example,
+ * ImageButton).
+ *
+ * @param contentDescription The content description.
+ *
+ * @attr ref android.R.styleable#View_contentDescription
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ }
+
+ /**
* Invoked whenever this view loses focus, either by losing window focus or by losing
* focus within its window. This method can be used to clear any state tied to the
* focus. For instance, if a button is held pressed with the trackball and the window
@@ -3222,11 +3326,37 @@
* @param direction The direction of the focus
*/
public void addFocusables(ArrayList<View> views, int direction) {
- if (!isFocusable()) return;
+ addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
+ }
- if (isInTouchMode() && !isFocusableInTouchMode()) return;
+ /**
+ * Adds any focusable views that are descendants of this view (possibly
+ * including this view if it is focusable itself) to views. This method
+ * adds all focusable views regardless if we are in touch mode or
+ * only views focusable in touch mode if we are in touch mode depending on
+ * the focusable mode paramater.
+ *
+ * @param views Focusable views found so far or null if all we are interested is
+ * the number of focusables.
+ * @param direction The direction of the focus.
+ * @param focusableMode The type of focusables to be added.
+ *
+ * @see #FOCUSABLES_ALL
+ * @see #FOCUSABLES_TOUCH_MODE
+ */
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (!isFocusable()) {
+ return;
+ }
- views.add(this);
+ if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&
+ isInTouchMode() && !isFocusableInTouchMode()) {
+ return;
+ }
+
+ if (views != null) {
+ views.add(this);
+ }
}
/**
@@ -8378,7 +8508,12 @@
* calling up the hierarchy.
*/
final Rect mTmpInvalRect = new Rect();
-
+
+ /**
+ * Temporary list for use in collecting focusable descendents of a view.
+ */
+ final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
+
/**
* Creates a new set of attachment information with the specified
* events handler and thread.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index db5177f..bf04dcd 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -24,15 +24,16 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.Region;
import android.graphics.RectF;
+import android.graphics.Region;
import android.os.Parcelable;
import android.os.SystemClock;
import android.util.AttributeSet;
+import android.util.Config;
import android.util.EventLog;
import android.util.Log;
import android.util.SparseArray;
-import android.util.Config;
+import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
@@ -601,6 +602,14 @@
*/
@Override
public void addFocusables(ArrayList<View> views, int direction) {
+ addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
final int focusableCount = views.size();
final int descendantFocusability = getDescendantFocusability();
@@ -612,7 +621,7 @@
for (int i = 0; i < count; i++) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
- child.addFocusables(views, direction);
+ child.addFocusables(views, direction, focusableMode);
}
}
}
@@ -625,7 +634,7 @@
descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
// No focusable descendants
(focusableCount == views.size())) {
- super.addFocusables(views, direction);
+ super.addFocusables(views, direction, focusableMode);
}
}
@@ -1020,6 +1029,15 @@
}
}
+ @Override
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+ boolean populated = false;
+ for (int i = 0, count = getChildCount(); i < count; i++) {
+ populated |= getChildAt(i).dispatchPopulateAccessibilityEvent(event);
+ }
+ return populated;
+ }
+
/**
* {@inheritDoc}
*/
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index dbfb194..7cd65e2 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -34,6 +34,8 @@
import android.util.EventLog;
import android.util.SparseArray;
import android.view.View.MeasureSpec;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.widget.Scroller;
@@ -640,6 +642,7 @@
host.dispatchAttachedToWindow(attachInfo, 0);
getRunQueue().executeActions(attachInfo.mHandler);
//Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
+
} else {
desiredWindowWidth = mWinFrame.width();
desiredWindowHeight = mWinFrame.height();
@@ -1723,7 +1726,7 @@
}
mView.dispatchWindowFocusChanged(hasWindowFocus);
}
-
+
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
if (hasWindowFocus) {
@@ -1741,6 +1744,10 @@
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
}
+
+ if (hasWindowFocus && mView != null) {
+ sendAccessibilityEvents();
+ }
}
} break;
case DIE:
@@ -2526,6 +2533,21 @@
sendMessage(msg);
}
+ /**
+ * The window is getting focus so if there is anything focused/selected
+ * send an {@link AccessibilityEvent} to announce that.
+ */
+ private void sendAccessibilityEvents() {
+ if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
+ return;
+ }
+ mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ View focusedView = mView.findFocus();
+ if (focusedView != null && focusedView != mView) {
+ focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+ }
+
public boolean showContextMenuForChild(View originalView) {
return false;
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 428de67..b0e738c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -24,7 +24,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
-import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
/**
* Abstract base class for a top-level window look and behavior policy. An
@@ -153,7 +153,16 @@
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTrackballEvent(MotionEvent event);
-
+
+ /**
+ * Called to process population of {@link AccessibilityEvent}s.
+ *
+ * @param event The event.
+ *
+ * @return boolean Return true if event population was completed.
+ */
+ public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event);
+
/**
* Instantiate the view to display in the panel for 'featureId'.
* You can return null, in which case the default content (typically
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 755d7b8..0973599 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -173,7 +173,6 @@
mRoots[index] = root;
mParams[index] = wparams;
}
-
// do this last because it fires off messages to start doing things
root.setView(view, wparams, panelParentView);
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.aidl b/core/java/android/view/accessibility/AccessibilityEvent.aidl
new file mode 100644
index 0000000..cee3604
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityEvent.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2009, 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 android.view.accessibility;
+
+parcelable AccessibilityEvent;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
new file mode 100644
index 0000000..c22f991
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -0,0 +1,734 @@
+/*
+ * Copyright (C) 2009 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 android.view.accessibility;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents accessibility events that are sent by the system when
+ * something notable happens in the user interface. For example, when a
+ * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
+ * <p>
+ * This class represents various semantically different accessibility event
+ * types. Each event type has associated a set of related properties. In other
+ * words, each event type is characterized via a subset of the properties exposed
+ * by this class. For each event type there is a corresponding constant defined
+ * in this class. Since some event types are semantically close there are mask
+ * constants that group them together. Follows a specification of the event
+ * types and their associated properties:
+ * <p>
+ * <b>VIEW TYPES</b> <br>
+ * <p>
+ * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View}
+ * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
+ * Type:{@link #TYPE_VIEW_CLICKED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()},
+ * {@link #isChecked()},
+ * {@link #isEnabled()},
+ * {@link #isPassword()},
+ * {@link #getItemCount()},
+ * {@link #getCurrentItemIndex()}
+ * <p>
+ * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View}
+ * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
+ * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()},
+ * {@link #isChecked()},
+ * {@link #isEnabled()},
+ * {@link #isPassword()},
+ * {@link #getItemCount()},
+ * {@link #getCurrentItemIndex()}
+ * <p>
+ * <b>View selected</b> - represents the event of selecting an item usually in
+ * the context of an {@link android.widget.AdapterView}. <br>
+ * Type: {@link #TYPE_VIEW_SELECTED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()},
+ * {@link #isChecked()},
+ * {@link #isEnabled()},
+ * {@link #isPassword()},
+ * {@link #getItemCount()},
+ * {@link #getCurrentItemIndex()}
+ * <p>
+ * <b>View focused</b> - represents the event of focusing a
+ * {@link android.view.View}. <br>
+ * Type: {@link #TYPE_VIEW_FOCUSED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()},
+ * {@link #isChecked()},
+ * {@link #isEnabled()},
+ * {@link #isPassword()},
+ * {@link #getItemCount()},
+ * {@link #getCurrentItemIndex()}
+ * <p>
+ * <b>View text changed</b> - represents the event of changing the text of an
+ * {@link android.widget.EditText}. <br>
+ * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()},
+ * {@link #isChecked()},
+ * {@link #isEnabled()},
+ * {@link #isPassword()},
+ * {@link #getItemCount()},
+ * {@link #getCurrentItemIndex()},
+ * {@link #getFromIndex()},
+ * {@link #getAddedCount()},
+ * {@link #getRemovedCount()},
+ * {@link #getBeforeText()}
+ * <p>
+ * <b>TRANSITION TYPES</b> <br>
+ * <p>
+ * <b>Window state changed</b> - represents the event of opening/closing a
+ * {@link android.widget.PopupWindow}, {@link android.view.Menu},
+ * {@link android.app.Dialog}, etc. <br>
+ * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()}
+ * <p>
+ * <b>NOTIFICATION TYPES</b> <br>
+ * <p>
+ * <b>Notification state changed</b> - represents the event showing/hiding
+ * {@link android.app.Notification}.
+ * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br>
+ * Properties:
+ * {@link #getClassName()},
+ * {@link #getPackageName()},
+ * {@link #getEventTime()},
+ * {@link #getText()}
+ * {@link #getParcelableData()}
+ * <p>
+ * <b>Security note</b>
+ * <p>
+ * Since an event contains the text of its source privacy can be compromised by leaking of
+ * sensitive information such as passwords. To address this issue any event fired in response
+ * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password.
+ *
+ * @see android.view.accessibility.AccessibilityManager
+ * @see android.accessibilityservice.AccessibilityService
+ */
+public final class AccessibilityEvent implements Parcelable {
+
+ /**
+ * Invalid selection/focus position.
+ *
+ * @see #getCurrentItemIndex()
+ */
+ public static final int INVALID_POSITION = -1;
+
+ /**
+ * Maximum length of the text fields.
+ *
+ * @see #getBeforeText()
+ * @see #getText()
+ */
+ public static final int MAX_TEXT_LENGTH = 500;
+
+ /**
+ * Represents the event of clicking on a {@link android.view.View} like
+ * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
+ */
+ public static final int TYPE_VIEW_CLICKED = 0x00000001;
+
+ /**
+ * Represents the event of long clicking on a {@link android.view.View} like
+ * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
+ */
+ public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
+
+ /**
+ * Represents the event of selecting an item usually in the context of an
+ * {@link android.widget.AdapterView}.
+ */
+ public static final int TYPE_VIEW_SELECTED = 0x00000004;
+
+ /**
+ * Represents the event of focusing a {@link android.view.View}.
+ */
+ public static final int TYPE_VIEW_FOCUSED = 0x00000008;
+
+ /**
+ * Represents the event of changing the text of an {@link android.widget.EditText}.
+ */
+ public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
+
+ /**
+ * Represents the event of opening/closing a {@link android.widget.PopupWindow},
+ * {@link android.view.Menu}, {@link android.app.Dialog}, etc.
+ */
+ public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
+
+ /**
+ * Represents the event showing/hiding a {@link android.app.Notification}.
+ */
+ public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
+
+ /**
+ * Mask for {@link AccessibilityEvent} all types.
+ *
+ * @see #TYPE_VIEW_CLICKED
+ * @see #TYPE_VIEW_LONG_CLICKED
+ * @see #TYPE_VIEW_SELECTED
+ * @see #TYPE_VIEW_FOCUSED
+ * @see #TYPE_VIEW_TEXT_CHANGED
+ * @see #TYPE_WINDOW_STATE_CHANGED
+ * @see #TYPE_NOTIFICATION_STATE_CHANGED
+ */
+ public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
+
+ private static final int MAX_POOL_SIZE = 2;
+ private static final Object mPoolLock = new Object();
+ private static AccessibilityEvent sPool;
+ private static int sPoolSize;
+
+ private static final int CHECKED = 0x00000001;
+ private static final int ENABLED = 0x00000002;
+ private static final int PASSWORD = 0x00000004;
+ private static final int FULL_SCREEN = 0x00000080;
+
+ private AccessibilityEvent mNext;
+
+ private int mEventType;
+ private int mBooleanProperties;
+ private int mCurrentItemIndex;
+ private int mItemCount;
+ private int mFromIndex;
+ private int mAddedCount;
+ private int mRemovedCount;
+
+ private long mEventTime;
+
+ private CharSequence mClassName;
+ private CharSequence mPackageName;
+ private CharSequence mContentDescription;
+ private CharSequence mBeforeText;
+
+ private Parcelable mParcelableData;
+
+ private final List<CharSequence> mText = new ArrayList<CharSequence>();
+
+ private boolean mIsInPool;
+
+ /*
+ * Hide constructor from clients.
+ */
+ private AccessibilityEvent() {
+ mCurrentItemIndex = INVALID_POSITION;
+ }
+
+ /**
+ * Gets if the source is checked.
+ *
+ * @return True if the view is checked, false otherwise.
+ */
+ public boolean isChecked() {
+ return getBooleanProperty(CHECKED);
+ }
+
+ /**
+ * Sets if the source is checked.
+ *
+ * @param isChecked True if the view is checked, false otherwise.
+ */
+ public void setChecked(boolean isChecked) {
+ setBooleanProperty(CHECKED, isChecked);
+ }
+
+ /**
+ * Gets if the source is enabled.
+ *
+ * @return True if the view is enabled, false otherwise.
+ */
+ public boolean isEnabled() {
+ return getBooleanProperty(ENABLED);
+ }
+
+ /**
+ * Sets if the source is enabled.
+ *
+ * @param isEnabled True if the view is enabled, false otherwise.
+ */
+ public void setEnabled(boolean isEnabled) {
+ setBooleanProperty(ENABLED, isEnabled);
+ }
+
+ /**
+ * Gets if the source is a password field.
+ *
+ * @return True if the view is a password field, false otherwise.
+ */
+ public boolean isPassword() {
+ return getBooleanProperty(PASSWORD);
+ }
+
+ /**
+ * Sets if the source is a password field.
+ *
+ * @param isPassword True if the view is a password field, false otherwise.
+ */
+ public void setPassword(boolean isPassword) {
+ setBooleanProperty(PASSWORD, isPassword);
+ }
+
+ /**
+ * Sets if the source is taking the entire screen.
+ *
+ * @param isFullScreen True if the source is full screen, false otherwise.
+ */
+ public void setFullScreen(boolean isFullScreen) {
+ setBooleanProperty(FULL_SCREEN, isFullScreen);
+ }
+
+ /**
+ * Gets if the source is taking the entire screen.
+ *
+ * @return True if the source is full screen, false otherwise.
+ */
+ public boolean isFullScreen() {
+ return getBooleanProperty(FULL_SCREEN);
+ }
+
+ /**
+ * Gets the event type.
+ *
+ * @return The event type.
+ */
+ public int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * Sets the event type.
+ *
+ * @param eventType The event type.
+ */
+ public void setEventType(int eventType) {
+ mEventType = eventType;
+ }
+
+ /**
+ * Gets the number of items that can be visited.
+ *
+ * @return The number of items.
+ */
+ public int getItemCount() {
+ return mItemCount;
+ }
+
+ /**
+ * Sets the number of items that can be visited.
+ *
+ * @param itemCount The number of items.
+ */
+ public void setItemCount(int itemCount) {
+ mItemCount = itemCount;
+ }
+
+ /**
+ * Gets the index of the source in the list of items the can be visited.
+ *
+ * @return The current item index.
+ */
+ public int getCurrentItemIndex() {
+ return mCurrentItemIndex;
+ }
+
+ /**
+ * Sets the index of the source in the list of items that can be visited.
+ *
+ * @param currentItemIndex The current item index.
+ */
+ public void setCurrentItemIndex(int currentItemIndex) {
+ mCurrentItemIndex = currentItemIndex;
+ }
+
+ /**
+ * Gets the index of the first character of the changed sequence.
+ *
+ * @return The index of the first character.
+ */
+ public int getFromIndex() {
+ return mFromIndex;
+ }
+
+ /**
+ * Sets the index of the first character of the changed sequence.
+ *
+ * @param fromIndex The index of the first character.
+ */
+ public void setFromIndex(int fromIndex) {
+ mFromIndex = fromIndex;
+ }
+
+ /**
+ * Gets the number of added characters.
+ *
+ * @return The number of added characters.
+ */
+ public int getAddedCount() {
+ return mAddedCount;
+ }
+
+ /**
+ * Sets the number of added characters.
+ *
+ * @param addedCount The number of added characters.
+ */
+ public void setAddedCount(int addedCount) {
+ mAddedCount = addedCount;
+ }
+
+ /**
+ * Gets the number of removed characters.
+ *
+ * @return The number of removed characters.
+ */
+ public int getRemovedCount() {
+ return mRemovedCount;
+ }
+
+ /**
+ * Sets the number of removed characters.
+ *
+ * @param removedCount The number of removed characters.
+ */
+ public void setRemovedCount(int removedCount) {
+ mRemovedCount = removedCount;
+ }
+
+ /**
+ * Gets the time in which this event was sent.
+ *
+ * @return The event time.
+ */
+ public long getEventTime() {
+ return mEventTime;
+ }
+
+ /**
+ * Sets the time in which this event was sent.
+ *
+ * @param eventTime The event time.
+ */
+ public void setEventTime(long eventTime) {
+ mEventTime = eventTime;
+ }
+
+ /**
+ * Gets the class name of the source.
+ *
+ * @return The class name.
+ */
+ public CharSequence getClassName() {
+ return mClassName;
+ }
+
+ /**
+ * Sets the class name of the source.
+ *
+ * @param className The lass name.
+ */
+ public void setClassName(CharSequence className) {
+ mClassName = className;
+ }
+
+ /**
+ * Gets the package name of the source.
+ *
+ * @return The package name.
+ */
+ public CharSequence getPackageName() {
+ return mPackageName;
+ }
+
+ /**
+ * Sets the package name of the source.
+ *
+ * @param packageName The package name.
+ */
+ public void setPackageName(CharSequence packageName) {
+ mPackageName = packageName;
+ }
+
+ /**
+ * Gets the text of the event. The index in the list represents the priority
+ * of the text. Specifically, the lower the index the higher the priority.
+ *
+ * @return The text.
+ */
+ public List<CharSequence> getText() {
+ return mText;
+ }
+
+ /**
+ * Sets the text before a change.
+ *
+ * @return The text before the change.
+ */
+ public CharSequence getBeforeText() {
+ return mBeforeText;
+ }
+
+ /**
+ * Sets the text before a change.
+ *
+ * @param beforeText The text before the change.
+ */
+ public void setBeforeText(CharSequence beforeText) {
+ mBeforeText = beforeText;
+ }
+
+ /**
+ * Gets the description of the source.
+ *
+ * @return The description.
+ */
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
+ * Sets the description of the source.
+ *
+ * @param contentDescription The description.
+ */
+ public void setContentDescription(CharSequence contentDescription) {
+ mContentDescription = contentDescription;
+ }
+
+ /**
+ * Gets the {@link Parcelable} data.
+ *
+ * @return The parcelable data.
+ */
+ public Parcelable getParcelableData() {
+ return mParcelableData;
+ }
+
+ /**
+ * Sets the {@link Parcelable} data of the event.
+ *
+ * @param parcelableData The parcelable data.
+ */
+ public void setParcelableData(Parcelable parcelableData) {
+ mParcelableData = parcelableData;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * instantiated with type property set.
+ *
+ * @param eventType The event type.
+ * @return An instance.
+ */
+ public static AccessibilityEvent obtain(int eventType) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.setEventType(eventType);
+ return event;
+ }
+
+ /**
+ * Returns a cached instance if such is available or a new one is
+ * instantiated.
+ *
+ * @return An instance.
+ */
+ public static AccessibilityEvent obtain() {
+ synchronized (mPoolLock) {
+ if (sPool != null) {
+ AccessibilityEvent event = sPool;
+ sPool = sPool.mNext;
+ sPoolSize--;
+ event.mNext = null;
+ event.mIsInPool = false;
+ return event;
+ }
+ return new AccessibilityEvent();
+ }
+ }
+
+ /**
+ * Return an instance back to be reused.
+ * <p>
+ * <b>Note: You must not touch the object after calling this function.</b>
+ */
+ public void recycle() {
+ if (mIsInPool) {
+ return;
+ }
+
+ clear();
+ synchronized (mPoolLock) {
+ if (sPoolSize <= MAX_POOL_SIZE) {
+ mNext = sPool;
+ sPool = this;
+ mIsInPool = true;
+ sPoolSize++;
+ }
+ }
+ }
+
+ /**
+ * Clears the state of this instance.
+ */
+ private void clear() {
+ mEventType = 0;
+ mBooleanProperties = 0;
+ mCurrentItemIndex = INVALID_POSITION;
+ mItemCount = 0;
+ mFromIndex = 0;
+ mAddedCount = 0;
+ mRemovedCount = 0;
+ mEventTime = 0;
+ mClassName = null;
+ mPackageName = null;
+ mContentDescription = null;
+ mBeforeText = null;
+ mText.clear();
+ }
+
+ /**
+ * Gets the value of a boolean property.
+ *
+ * @param property The property.
+ * @return The value.
+ */
+ private boolean getBooleanProperty(int property) {
+ return (mBooleanProperties & property) == property;
+ }
+
+ /**
+ * Sets a boolean property.
+ *
+ * @param property The property.
+ * @param value The value.
+ */
+ private void setBooleanProperty(int property, boolean value) {
+ if (value) {
+ mBooleanProperties |= property;
+ } else {
+ mBooleanProperties &= ~property;
+ }
+ }
+
+ /**
+ * Creates a new instance from a {@link Parcel}.
+ *
+ * @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
+ */
+ public void initFromParcel(Parcel parcel) {
+ mEventType = parcel.readInt();
+ mBooleanProperties = parcel.readInt();
+ mCurrentItemIndex = parcel.readInt();
+ mItemCount = parcel.readInt();
+ mFromIndex = parcel.readInt();
+ mAddedCount = parcel.readInt();
+ mRemovedCount = parcel.readInt();
+ mEventTime = parcel.readLong();
+ mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+ mParcelableData = parcel.readParcelable(null);
+ parcel.readList(mText, null);
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mEventType);
+ parcel.writeInt(mBooleanProperties);
+ parcel.writeInt(mCurrentItemIndex);
+ parcel.writeInt(mItemCount);
+ parcel.writeInt(mFromIndex);
+ parcel.writeInt(mAddedCount);
+ parcel.writeInt(mRemovedCount);
+ parcel.writeLong(mEventTime);
+ TextUtils.writeToParcel(mClassName, parcel, 0);
+ TextUtils.writeToParcel(mPackageName, parcel, 0);
+ TextUtils.writeToParcel(mContentDescription, parcel, 0);
+ TextUtils.writeToParcel(mBeforeText, parcel, 0);
+ parcel.writeParcelable(mParcelableData, flags);
+ parcel.writeList(mText);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(super.toString());
+ builder.append("; EventType: " + mEventType);
+ builder.append("; EventTime: " + mEventTime);
+ builder.append("; ClassName: " + mClassName);
+ builder.append("; PackageName: " + mPackageName);
+ builder.append("; Text: " + mText);
+ builder.append("; ContentDescription: " + mContentDescription);
+ builder.append("; ItemCount: " + mItemCount);
+ builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
+ builder.append("; IsEnabled: " + isEnabled());
+ builder.append("; IsPassword: " + isPassword());
+ builder.append("; IsChecked: " + isChecked());
+ builder.append("; IsFullScreen: " + isFullScreen());
+ builder.append("; BeforeText: " + mBeforeText);
+ builder.append("; FromIndex: " + mFromIndex);
+ builder.append("; AddedCount: " + mAddedCount);
+ builder.append("; RemovedCount: " + mRemovedCount);
+ builder.append("; ParcelableData: " + mParcelableData);
+ return builder.toString();
+ }
+
+ /**
+ * @see Parcelable.Creator
+ */
+ public static final Parcelable.Creator<AccessibilityEvent> CREATOR =
+ new Parcelable.Creator<AccessibilityEvent>() {
+ public AccessibilityEvent createFromParcel(Parcel parcel) {
+ AccessibilityEvent event = AccessibilityEvent.obtain();
+ event.initFromParcel(parcel);
+ return event;
+ }
+
+ public AccessibilityEvent[] newArray(int size) {
+ return new AccessibilityEvent[size];
+ }
+ };
+}
diff --git a/core/java/android/view/accessibility/AccessibilityEventSource.java b/core/java/android/view/accessibility/AccessibilityEventSource.java
new file mode 100644
index 0000000..3d70959
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityEventSource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 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 android.view.accessibility;
+
+/**
+ * This interface is implemented by classes source of {@link AccessibilityEvent}s.
+ */
+public interface AccessibilityEventSource {
+
+ /**
+ * Handles the request for sending an {@link AccessibilityEvent} given
+ * the event type. The method must first check if accessibility is on
+ * via calling {@link AccessibilityManager#isEnabled()}, obtain
+ * an {@link AccessibilityEvent} from the event pool through calling
+ * {@link AccessibilityEvent#obtain(int)}, populate the event, and
+ * send it for dispatch via calling
+ * {@link AccessibilityManager#sendAccessibilityEvent(AccessibilityEvent)}.
+ *
+ * @see AccessibilityEvent
+ * @see AccessibilityManager
+ *
+ * @param eventType The event type.
+ */
+ public void sendAccessibilityEvent(int eventType);
+
+ /**
+ * Handles the request for sending an {@link AccessibilityEvent}. The
+ * method does not guarantee to check if accessibility is on before
+ * sending the event for dispatch. It is responsibility of the caller
+ * to do the check via calling {@link AccessibilityManager#isEnabled()}.
+ *
+ * @see AccessibilityEvent
+ * @see AccessibilityManager
+ *
+ * @param event The event.
+ */
+ public void sendAccessibilityEventUnchecked(AccessibilityEvent event);
+}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
new file mode 100644
index 0000000..0186270
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2009 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 android.view.accessibility;
+
+import static android.util.Config.LOGV;
+
+import android.content.Context;
+import android.content.pm.ServiceInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
+ * Such events are generated when something notable happens in the user interface,
+ * for example an {@link android.app.Activity} starts, the focus or selection of a
+ * {@link android.view.View} changes etc. Parties interested in handling accessibility
+ * events implement and register an accessibility service which extends
+ * {@link android.accessibilityservice.AccessibilityService}.
+ *
+ * @see AccessibilityEvent
+ * @see android.accessibilityservice.AccessibilityService
+ * @see android.content.Context#getSystemService
+ */
+public final class AccessibilityManager {
+ private static final String LOG_TAG = "AccessibilityManager";
+
+ static final Object sInstanceSync = new Object();
+
+ private static AccessibilityManager sInstance;
+
+ private static final int DO_SET_ENABLED = 10;
+
+ final IAccessibilityManager mService;
+
+ final Handler mHandler;
+
+ boolean mIsEnabled;
+
+ final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
+ public void setEnabled(boolean enabled) {
+ mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget();
+ }
+ };
+
+ class MyHandler extends Handler {
+
+ MyHandler(Looper mainLooper) {
+ super(mainLooper);
+ }
+
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case DO_SET_ENABLED :
+ synchronized (mHandler) {
+ mIsEnabled = (message.arg1 == 1);
+ }
+ return;
+ default :
+ Log.w(LOG_TAG, "Unknown message type: " + message.what);
+ }
+ }
+ }
+
+ /**
+ * Get an AccessibilityManager instance (create one if necessary).
+ *
+ * @hide
+ */
+ public static AccessibilityManager getInstance(Context context) {
+ synchronized (sInstanceSync) {
+ if (sInstance == null) {
+ sInstance = new AccessibilityManager(context);
+ }
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create an instance.
+ *
+ * @param context A {@link Context}.
+ */
+ private AccessibilityManager(Context context) {
+ mHandler = new MyHandler(context.getMainLooper());
+ IBinder iBinder = ServiceManager.getService(Context.ACCESSIBILITY_SERVICE);
+ mService = IAccessibilityManager.Stub.asInterface(iBinder);
+ try {
+ mService.addClient(mClient);
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
+ }
+ }
+
+ /**
+ * Returns if the {@link AccessibilityManager} is enabled.
+ *
+ * @return True if this {@link AccessibilityManager} is enabled, false otherwise.
+ */
+ public boolean isEnabled() {
+ synchronized (mHandler) {
+ return mIsEnabled;
+ }
+ }
+
+ /**
+ * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not
+ * enabled the call is a NOOP.
+ *
+ * @param event The {@link AccessibilityEvent}.
+ *
+ * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent}
+ * while accessibility is not enabled.
+ */
+ public void sendAccessibilityEvent(AccessibilityEvent event) {
+ if (!mIsEnabled) {
+ throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ }
+ boolean doRecycle = false;
+ try {
+ event.setEventTime(SystemClock.uptimeMillis());
+ // it is possible that this manager is in the same process as the service but
+ // client using it is called through Binder from another process. Example: MMS
+ // app adds a SMS notification and the NotificationManagerService calls this method
+ long identityToken = Binder.clearCallingIdentity();
+ doRecycle = mService.sendAccessibilityEvent(event);
+ Binder.restoreCallingIdentity(identityToken);
+ if (LOGV) {
+ Log.i(LOG_TAG, event + " sent");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error during sending " + event + " ", re);
+ } finally {
+ if (doRecycle) {
+ event.recycle();
+ }
+ }
+ }
+
+ /**
+ * Requests interruption of the accessibility feedback from all accessibility services.
+ */
+ public void interrupt() {
+ if (!mIsEnabled) {
+ throw new IllegalStateException("Accessibility off. Did you forget to check that?");
+ }
+ try {
+ mService.interrupt();
+ if (LOGV) {
+ Log.i(LOG_TAG, "Requested interrupt from all services");
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while requesting interrupt from all services. ", re);
+ }
+ }
+
+ /**
+ * Returns the {@link ServiceInfo}s of the installed accessibility services.
+ *
+ * @return An unmodifiable list with {@link ServiceInfo}s.
+ */
+ public List<ServiceInfo> getAccessibilityServiceList() {
+ List<ServiceInfo> services = null;
+ try {
+ services = mService.getAccessibilityServiceList();
+ if (LOGV) {
+ Log.i(LOG_TAG, "Installed AccessibilityServices " + services);
+ }
+ } catch (RemoteException re) {
+ Log.e(LOG_TAG, "Error while obtaining the installed AccessibilityServices. ", re);
+ }
+ return Collections.unmodifiableList(services);
+ }
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
new file mode 100644
index 0000000..32788be
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -0,0 +1,39 @@
+/* //device/java/android/android/app/INotificationManager.aidl
+**
+** Copyright 2009, 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 android.view.accessibility;
+
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.IAccessibilityManagerClient;
+import android.content.pm.ServiceInfo;
+
+/**
+ * Interface implemented by the AccessibilityManagerService called by
+ * the AccessibilityMasngers.
+ *
+ * @hide
+ */
+interface IAccessibilityManager {
+
+ void addClient(IAccessibilityManagerClient client);
+
+ boolean sendAccessibilityEvent(in AccessibilityEvent uiEvent);
+
+ List<ServiceInfo> getAccessibilityServiceList();
+
+ void interrupt();
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
new file mode 100644
index 0000000..1eb60fc
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2009 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 android.view.accessibility;
+
+/**
+ * Interface a client of the IAccessibilityManager implements to
+ * receive information about changes in the manager state.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityManagerClient {
+
+ void setEnabled(boolean enabled);
+
+}
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.util.AttributeSet;
import android.util.SparseArray;
import android.view.ContextMenu;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.ViewDebug;
import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
+import android.view.accessibility.AccessibilityEvent;
/**
@@ -618,7 +619,9 @@
}
/**
- * 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 @@
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 @@
}
@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 @@
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.Parcelable;
import android.util.AttributeSet;
import android.view.Gravity;
-
+import android.view.accessibility.AccessibilityEvent;
/**
* <p>
@@ -124,6 +124,7 @@
if (mOnCheckedChangeWidgetListener != null) {
mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked);
}
+
mBroadcasting = false;
}
}
@@ -205,6 +206,25 @@
}
@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.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 int getBaseline() {
return mBaselineAligned ? getMeasuredHeight() : -1;
}
-
+
/**
* Set a tinting option for the image.
*
@@ -878,7 +880,7 @@
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.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 @@
}
}
+ @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 @@
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.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 @@
unregisterForScrollChanged();
mWindowManager.removeView(mPopupView);
+
if (mPopupView != mContentView && mPopupView instanceof ViewGroup) {
((ViewGroup) mPopupView).removeView(mContentView);
}
@@ -1316,7 +1317,16 @@
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 android.content.Context;
-import android.content.res.TypedArray;
-import android.util.AttributeSet;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.Gravity;
-import android.view.ViewDebug;
-import android.widget.RemoteViews.RemoteView;
-import android.graphics.Rect;
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.Gravity;
+import android.view.View;
+import android.view.ViewDebug;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.widget.RemoteViews.RemoteView;
+
+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 @@
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 @@
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 @@
openDrawer();
invalidate();
requestLayout();
+
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
/**
@@ -777,6 +780,7 @@
scrollListener.onScrollStarted();
}
animateClose(mVertical ? mHandle.getTop() : mHandle.getLeft());
+
if (scrollListener != null) {
scrollListener.onScrollEnded();
}
@@ -798,6 +802,9 @@
scrollListener.onScrollStarted();
}
animateOpen(mVertical ? mHandle.getTop() : mHandle.getLeft());
+
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+
if (scrollListener != null) {
scrollListener.onScrollEnded();
}
@@ -827,6 +834,7 @@
}
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.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.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.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.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 @@
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 @@
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 @@
}
@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.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 @@
}
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 @@
TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
+
mView = null;
}
}
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 633a831..43eec1b 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1165,6 +1165,11 @@
enabled for events such as long presses. -->
<attr name="hapticFeedbackEnabled" format="boolean" />
+ <!-- Defines text that briefly describes content of the view. This property is used
+ primarily for accessibility. Since some views do not have textual
+ representation this attribute can be used for providing such. -->
+ <attr name="contentDescription" format="string" localization="suggested" />
+
<!-- Name of the method in this View's context to invoke when the view is
clicked. This name must correspond to a public method that takes
exactly one parameter of type View. For instance, if you specify
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index f90c6b8..b5808ea 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1100,6 +1100,7 @@
<public type="attr" name="targetSdkVersion" id="0x01010270" />
<public type="attr" name="maxSdkVersion" id="0x01010271" />
<public type="attr" name="testOnly" id="0x01010272" />
+ <public type="attr" name="contentDescription" id="0x01010273" />
<public type="anim" name="anticipate_interpolator" id="0x010a0007" />
<public type="anim" name="overshoot_interpolator" id="0x010a0008" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 331ef1a..ed81cb4 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -887,7 +887,6 @@
properties uploaded by the checkin service. Not for use by normal
applications.</string>
-
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_bindGadget">choose widgets</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
@@ -2366,10 +2365,12 @@
<!-- This string array should be overridden by the manufacture to present a list of carrier-id,locale pairs. This is used at startup to set a default locale by checking the system property ro.carrier for the carrier-id and searching through this array -->
<string-array translatable="false" name="carrier_locales">
- </string-array>
+ </string-array>
+
+ <!-- Title for the selected state of a CompoundButton. -->
+ <string name="accessibility_compound_button_selected">checked</string>
+
+ <!-- Title for the unselected state of a CompoundButton. -->
+ <string name="accessibility_compound_button_unselected">not checked</string>
+
</resources>
-
-
-
-
-
diff --git a/services/java/com/android/server/AccessibilityManagerService.java b/services/java/com/android/server/AccessibilityManagerService.java
new file mode 100644
index 0000000..c205fc0
--- /dev/null
+++ b/services/java/com/android/server/AccessibilityManagerService.java
@@ -0,0 +1,668 @@
+/*
+ ** Copyright 2009, 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.android.server;
+
+import static android.util.Config.LOGV;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.HandlerCaller.SomeArgs;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.IEventListener;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.IAccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class is instantiated by the system as a system level service and can be
+ * accessed only by the system. The task of this service is to be a centralized
+ * event dispatch for {@link AccessibilityEvent}s generated across all processes
+ * on the device. Events are dispatched to {@link AccessibilityService}s.
+ *
+ * @hide
+ */
+public class AccessibilityManagerService extends IAccessibilityManager.Stub
+ implements HandlerCaller.Callback {
+
+ private static final String LOG_TAG = "AccessibilityManagerService";
+
+ private static int sIdCounter = 0;
+
+ private static final int OWN_PROCESS_ID = android.os.Process.myPid();
+
+ private static final int DO_SET_SERVICE_INFO = 10;
+
+ final HandlerCaller mCaller;
+
+ final Context mContext;
+
+ final Object mLock = new Object();
+
+ final List<Service> mServices = new ArrayList<Service>();
+
+ final List<IAccessibilityManagerClient> mClients =
+ new ArrayList<IAccessibilityManagerClient>();
+
+ final Map<ComponentName, Service> mComponentNameToServiceMap =
+ new HashMap<ComponentName, Service>();
+
+ private final List<ServiceInfo> mInstalledServices = new ArrayList<ServiceInfo>();
+
+ private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
+
+ private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
+
+ private PackageManager mPackageManager;
+
+ private int mHandledFeedbackTypes = 0;
+
+ private boolean mIsEnabled;
+
+ /**
+ * Handler for delayed event dispatch.
+ */
+ private Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message message) {
+ Service service = (Service) message.obj;
+ int eventType = message.arg1;
+
+ synchronized (mLock) {
+ notifyEventListenerLocked(service, eventType);
+ AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
+ service.mPendingEvents.remove(eventType);
+ tryRecycleLocked(oldEvent);
+ }
+ }
+ };
+
+ /**
+ * Creates a new instance.
+ *
+ * @param context A {@link Context} instance.
+ */
+ AccessibilityManagerService(Context context) {
+ mContext = context;
+ mPackageManager = mContext.getPackageManager();
+ mCaller = new HandlerCaller(context, this);
+
+ registerPackageChangeAndBootCompletedBroadcastReceiver();
+ registerSettingsContentObservers();
+
+ synchronized (mLock) {
+ populateAccessibilityServiceListLocked();
+ }
+ }
+
+ /**
+ * Registers a {@link BroadcastReceiver} for the events of
+ * adding/changing/removing/restarting a package and boot completion.
+ */
+ private void registerPackageChangeAndBootCompletedBroadcastReceiver() {
+ Context context = mContext;
+
+ BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mLock) {
+ populateAccessibilityServiceListLocked();
+ manageServicesLocked();
+
+ if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
+ mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+ updateClientsLocked();
+ }
+ }
+ }
+ };
+
+ // package changes
+ IntentFilter packageFilter = new IntentFilter();
+ packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ packageFilter.addDataScheme("package");
+ context.registerReceiver(broadcastReceiver, packageFilter);
+
+ // boot completed
+ IntentFilter bootFiler = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ mContext.registerReceiver(broadcastReceiver, bootFiler);
+ }
+
+ /**
+ * {@link ContentObserver}s for {@link Settings.Secure#ACCESSIBILITY_ENABLED}
+ * and {@link Settings.Secure#ENABLED_ACCESSIBILITY_SERVICES} settings.
+ */
+ private void registerSettingsContentObservers() {
+ ContentResolver contentResolver = mContext.getContentResolver();
+
+ Uri enabledUri = Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_ENABLED);
+ contentResolver.registerContentObserver(enabledUri, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
+
+ synchronized (mLock) {
+ if (mIsEnabled) {
+ manageServicesLocked();
+ } else {
+ unbindAllServicesLocked();
+ }
+ updateClientsLocked();
+ }
+ }
+ });
+
+ Uri providersUri =
+ Settings.Secure.getUriFor(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+ contentResolver.registerContentObserver(providersUri, false,
+ new ContentObserver(new Handler()) {
+ @Override
+ public void onChange(boolean selfChange) {
+ super.onChange(selfChange);
+
+ synchronized (mLock) {
+ manageServicesLocked();
+ }
+ }
+ });
+ }
+
+ public void addClient(IAccessibilityManagerClient client) {
+ synchronized (mLock) {
+ try {
+ client.setEnabled(mIsEnabled);
+ mClients.add(client);
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Dead AccessibilityManagerClient: " + client, re);
+ }
+ }
+ }
+
+ public boolean sendAccessibilityEvent(AccessibilityEvent event) {
+ synchronized (mLock) {
+ notifyAccessibilityServicesDelayedLocked(event, false);
+ notifyAccessibilityServicesDelayedLocked(event, true);
+ }
+ // event not scheduled for dispatch => recycle
+ if (mHandledFeedbackTypes == 0) {
+ event.recycle();
+ } else {
+ mHandledFeedbackTypes = 0;
+ }
+
+ return (OWN_PROCESS_ID != Binder.getCallingPid());
+ }
+
+ public List<ServiceInfo> getAccessibilityServiceList() {
+ synchronized (mLock) {
+ return mInstalledServices;
+ }
+ }
+
+ public void interrupt() {
+ synchronized (mLock) {
+ for (int i = 0, count = mServices.size(); i < count; i++) {
+ Service service = mServices.get(i);
+ try {
+ service.mServiceInterface.onInterrupt();
+ } catch (RemoteException re) {
+ if (re instanceof DeadObjectException) {
+ Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
+ if (removeDeadServiceLocked(service)) {
+ count--;
+ i--;
+ }
+ } else {
+ Log.e(LOG_TAG, "Error during sending interrupt request to "
+ + service.mService, re);
+ }
+ }
+ }
+ }
+ }
+
+ public void executeMessage(Message message) {
+ switch (message.what) {
+ case DO_SET_SERVICE_INFO:
+ SomeArgs arguments = ((SomeArgs) message.obj);
+
+ AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
+ Service service = (Service) arguments.arg2;
+
+ synchronized (mLock) {
+ service.mEventTypes = info.eventTypes;
+ service.mFeedbackType = info.feedbackType;
+ String[] packageNames = info.packageNames;
+ if (packageNames != null) {
+ service.mPackageNames.addAll(Arrays.asList(packageNames));
+ }
+ service.mNotificationTimeout = info.notificationTimeout;
+ service.mIsDefault = (info.flags & AccessibilityServiceInfo.DEFAULT) != 0;
+ }
+ return;
+ default:
+ Log.w(LOG_TAG, "Unknown message type: " + message.what);
+ }
+ }
+
+ /**
+ * Populates the cached list of installed {@link AccessibilityService}s.
+ */
+ private void populateAccessibilityServiceListLocked() {
+ mInstalledServices.clear();
+
+ List<ResolveInfo> installedServices = mPackageManager.queryIntentServices(
+ new Intent(AccessibilityService.SERVICE_INTERFACE), PackageManager.GET_SERVICES);
+
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ mInstalledServices.add(installedServices.get(i).serviceInfo);
+ }
+ }
+
+ /**
+ * Performs {@link AccessibilityService}s delayed notification. The delay is configurable
+ * and denotes the period after the last event before notifying the service.
+ *
+ * @param event The event.
+ * @param isDefault True to notify default listeners, not default services.
+ */
+ private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
+ boolean isDefault) {
+ for (int i = 0, count = mServices.size(); i < count; i++) {
+ Service service = mServices.get(i);
+
+ if (service.mIsDefault == isDefault) {
+ if (canDispathEventLocked(service, event, mHandledFeedbackTypes)) {
+ mHandledFeedbackTypes |= service.mFeedbackType;
+ notifyAccessibilityServiceDelayedLocked(service, event);
+ }
+ }
+ }
+ }
+
+ /**
+ * Performs an {@link AccessibilityService} delayed notification. The delay is configurable
+ * and denotes the period after the last event before notifying the service.
+ *
+ * @param service The service.
+ * @param event The event.
+ */
+ private void notifyAccessibilityServiceDelayedLocked(Service service,
+ AccessibilityEvent event) {
+ synchronized (mLock) {
+ int eventType = event.getEventType();
+ AccessibilityEvent oldEvent = service.mPendingEvents.get(eventType);
+ service.mPendingEvents.put(eventType, event);
+
+ int what = eventType | (service.mId << 16);
+ if (oldEvent != null) {
+ mHandler.removeMessages(what);
+ tryRecycleLocked(oldEvent);
+ }
+
+ Message message = mHandler.obtainMessage(what, service);
+ message.arg1 = event.getEventType();
+ mHandler.sendMessageDelayed(message, service.mNotificationTimeout);
+ }
+ }
+
+ /**
+ * Recycles an event if it can be safely recycled. The condition is that no
+ * not notified service is interested in the event.
+ *
+ * @param event The event.
+ */
+ private void tryRecycleLocked(AccessibilityEvent event) {
+ int eventType = event.getEventType();
+ List<Service> services = mServices;
+
+ // linear in the number of service which is not large
+ for (int i = 0, count = services.size(); i < count; i++) {
+ Service service = services.get(i);
+ if (service.mPendingEvents.get(eventType) == event) {
+ return;
+ }
+ }
+
+ event.recycle();
+ }
+
+ /**
+ * Notifies a service for a scheduled event given the event type.
+ *
+ * @param service The service.
+ * @param eventType The type of the event to dispatch.
+ */
+ private void notifyEventListenerLocked(Service service, int eventType) {
+ IEventListener listener = service.mServiceInterface;
+ AccessibilityEvent event = service.mPendingEvents.get(eventType);
+
+ try {
+ listener.onAccessibilityEvent(event);
+ if (LOGV) {
+ Log.i(LOG_TAG, "Event " + event + " sent to " + listener);
+ }
+ } catch (RemoteException re) {
+ if (re instanceof DeadObjectException) {
+ Log.w(LOG_TAG, "Dead " + service.mService + ". Cleaning up.");
+ synchronized (mLock) {
+ removeDeadServiceLocked(service);
+ }
+ } else {
+ Log.e(LOG_TAG, "Error during sending " + event + " to " + service.mService, re);
+ }
+ }
+ }
+
+ /**
+ * Removes a dead service.
+ *
+ * @param service The service.
+ * @return True if the service was removed, false otherwise.
+ */
+ private boolean removeDeadServiceLocked(Service service) {
+ mServices.remove(service);
+ mHandler.removeMessages(service.mId);
+
+ if (LOGV) {
+ Log.i(LOG_TAG, "Dead service " + service.mService + " removed");
+ }
+
+ if (mServices.isEmpty()) {
+ mIsEnabled = false;
+ updateClientsLocked();
+ }
+
+ return true;
+ }
+
+ /**
+ * Determines if given event can be dispatched to a service based on the package of the
+ * event source and already notified services for that event type. Specifically, a
+ * service is notified if it is interested in events from the package and no other service
+ * providing the same feedback type has been notified. Exception are services the
+ * provide generic feedback (feedback type left as a safety net for unforeseen feedback
+ * types) which are always notified.
+ *
+ * @param service The potential receiver.
+ * @param event The event.
+ * @param handledFeedbackTypes The feedback types for which services have been notified.
+ * @return True if the listener should be notified, false otherwise.
+ */
+ private boolean canDispathEventLocked(Service service, AccessibilityEvent event,
+ int handledFeedbackTypes) {
+
+ if (!service.isConfigured()) {
+ return false;
+ }
+
+ if (!service.mService.isBinderAlive()) {
+ removeDeadServiceLocked(service);
+ return false;
+ }
+
+ int eventType = event.getEventType();
+ if ((service.mEventTypes & eventType) != eventType) {
+ return false;
+ }
+
+ Set<String> packageNames = service.mPackageNames;
+ CharSequence packageName = event.getPackageName();
+
+ if (packageNames.isEmpty() || packageNames.contains(packageName)) {
+ int feedbackType = service.mFeedbackType;
+ if ((handledFeedbackTypes & feedbackType) != feedbackType
+ || feedbackType == AccessibilityServiceInfo.FEEDBACK_GENERIC) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Manages services by starting enabled ones and stopping disabled ones.
+ */
+ private void manageServicesLocked() {
+ populateEnabledServicesLocked(mEnabledServices);
+ updateServicesStateLocked(mInstalledServices, mEnabledServices);
+ }
+
+ /**
+ * Unbinds all bound services.
+ */
+ private void unbindAllServicesLocked() {
+ List<Service> services = mServices;
+
+ for (int i = 0, count = services.size(); i < count; i++) {
+ Service service = services.get(i);
+
+ service.unbind();
+ mComponentNameToServiceMap.remove(service.mComponentName);
+ }
+ services.clear();
+ }
+
+ /**
+ * Populates a list with the {@link ComponentName}s of all enabled
+ * {@link AccessibilityService}s.
+ *
+ * @param enabledServices The list.
+ */
+ private void populateEnabledServicesLocked(Set<ComponentName> enabledServices) {
+ enabledServices.clear();
+
+ String servicesValue = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
+
+ if (servicesValue != null) {
+ TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+ splitter.setString(servicesValue);
+ while (splitter.hasNext()) {
+ ComponentName enabledService = ComponentName.unflattenFromString(splitter.next());
+ enabledServices.add(enabledService);
+ }
+ }
+ }
+
+ /**
+ * Updates the state of each service by starting (or keeping running) enabled ones and
+ * stopping the rest.
+ *
+ * @param installedServices All installed {@link AccessibilityService}s.
+ * @param enabledServices The {@link ComponentName}s of the enabled services.
+ */
+ private void updateServicesStateLocked(List<ServiceInfo> installedServices,
+ Set<ComponentName> enabledServices) {
+
+ Map<ComponentName, Service> componentNameToServiceMap = mComponentNameToServiceMap;
+ List<Service> services = mServices;
+
+ for (int i = 0, count = installedServices.size(); i < count; i++) {
+ ServiceInfo intalledService = installedServices.get(i);
+ ComponentName componentName = new ComponentName(intalledService.packageName,
+ intalledService.name);
+ Service service = componentNameToServiceMap.get(componentName);
+
+ if (enabledServices.contains(componentName)) {
+ if (service == null) {
+ new Service(componentName).bind();
+ }
+ } else {
+ if (service != null) {
+ service.unbind();
+ componentNameToServiceMap.remove(componentName);
+ services.remove(service);
+ }
+ }
+ }
+ }
+
+ /**
+ * Updates the state of {@link android.view.accessibility.AccessibilityManager} clients.
+ */
+ private void updateClientsLocked() {
+ for (int i = 0, count = mClients.size(); i < count; i++) {
+ try {
+ mClients.get(i).setEnabled(mIsEnabled);
+ } catch (RemoteException re) {
+ mClients.remove(i);
+ count--;
+ }
+ }
+ }
+
+ /**
+ * This class represents an accessibility service. It stores all per service
+ * data required for the service management, provides API for starting/stopping the
+ * service and is responsible for adding/removing the service in the data structures
+ * for service management. The class also exposes configuration interface that is
+ * passed to the service it represents as soon it is bound. It also serves as the
+ * connection for the service.
+ */
+ class Service extends IAccessibilityServiceConnection.Stub implements ServiceConnection {
+ int mId = 0;
+
+ IBinder mService;
+
+ IEventListener mServiceInterface;
+
+ int mEventTypes;
+
+ int mFeedbackType;
+
+ Set<String> mPackageNames = new HashSet<String>();
+
+ boolean mIsDefault;
+
+ long mNotificationTimeout;
+
+ boolean mIsActive;
+
+ ComponentName mComponentName;
+
+ Intent mIntent;
+
+ // the events pending events to be dispatched to this service
+ final SparseArray<AccessibilityEvent> mPendingEvents =
+ new SparseArray<AccessibilityEvent>();
+
+ Service(ComponentName componentName) {
+ mId = sIdCounter++;
+ mComponentName = componentName;
+ mIntent = new Intent().setComponent(mComponentName);
+ }
+
+ /**
+ * Binds to the accessibility service.
+ */
+ public void bind() {
+ if (mService == null) {
+ mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
+ }
+ }
+
+ /**
+ * Unbinds form the accessibility service and removes it from the data
+ * structures for service management.
+ */
+ public void unbind() {
+ if (mService != null) {
+ mContext.unbindService(this);
+ }
+ }
+
+ /**
+ * Returns if the service is configured i.e. at least event types of interest
+ * and feedback type must be set.
+ *
+ * @return True if the service is configured, false otherwise.
+ */
+ public boolean isConfigured() {
+ return (mEventTypes != 0 && mFeedbackType != 0);
+ }
+
+ public void setServiceInfo(AccessibilityServiceInfo info) {
+ mCaller.obtainMessageOO(DO_SET_SERVICE_INFO, info, this).sendToTarget();
+ }
+
+ public void onServiceConnected(ComponentName componentName, IBinder service) {
+ mService = service;
+ mServiceInterface = IEventListener.Stub.asInterface(service);
+
+ try {
+ mServiceInterface.setConnection(this);
+ synchronized (mLock) {
+ if (!mServices.contains(this)) {
+ mServices.add(this);
+ mComponentNameToServiceMap.put(componentName, this);
+ }
+ }
+ } catch (RemoteException re) {
+ Log.w(LOG_TAG, "Error while setting Controller for service: " + service, re);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName componentName) {
+ synchronized (mLock) {
+ Service service = mComponentNameToServiceMap.remove(componentName);
+ mServices.remove(service);
+ }
+ }
+ }
+}
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 6ed8b4c..4a2808b 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -16,6 +16,10 @@
package com.android.server;
+import com.android.server.status.IconData;
+import com.android.server.status.NotificationData;
+import com.android.server.status.StatusBarService;
+
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.INotificationManager;
@@ -30,33 +34,29 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
-import android.media.AudioManager;
import android.media.AsyncPlayer;
-import android.media.RingtoneManager;
+import android.media.AudioManager;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Binder;
-import android.os.RemoteException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Power;
+import android.os.RemoteException;
import android.os.Vibrator;
import android.provider.Settings;
-import android.util.Config;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
-import com.android.server.status.IconData;
-import com.android.server.status.NotificationData;
-import com.android.server.status.StatusBarService;
-
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
-import java.io.IOException;
class NotificationManagerService extends INotificationManager.Stub
{
@@ -98,7 +98,7 @@
private boolean mBatteryLow;
private boolean mBatteryFull;
private NotificationRecord mLedNotification;
-
+
private static final int BATTERY_LOW_ARGB = 0xFFFF0000; // Charging Low - red solid on
private static final int BATTERY_MEDIUM_ARGB = 0xFFFFFF00; // Charging - orange solid on
private static final int BATTERY_FULL_ARGB = 0xFF00FF00; // Charging Full - green solid on
@@ -594,6 +594,9 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ sendAccessibilityEventTypeNotificationChangedDoCheck(notification, pkg);
+
} else {
if (old != null && old.statusBarKey != null) {
long identity = Binder.clearCallingIdentity();
@@ -676,6 +679,26 @@
idOut[0] = id;
}
+ private void sendAccessibilityEventTypeNotificationChangedDoCheck(Notification notification,
+ CharSequence packageName) {
+ AccessibilityManager manager = AccessibilityManager.getInstance(mContext);
+ if (!manager.isEnabled()) {
+ return;
+ }
+
+ AccessibilityEvent event =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+ event.setPackageName(packageName);
+ event.setClassName(Notification.class.getName());
+ event.setParcelableData(notification);
+ CharSequence tickerText = notification.tickerText;
+ if (!TextUtils.isEmpty(tickerText)) {
+ event.getText().add(tickerText);
+ }
+
+ manager.sendAccessibilityEvent(event);
+ }
+
private void cancelNotificationLocked(NotificationRecord r) {
// status bar
if (r.notification.icon != 0) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8b7260b..f6c1525 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -228,6 +228,14 @@
}
try {
+ Log.i(TAG, "Starting Accessibility Manager.");
+ ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,
+ new AccessibilityManagerService(context));
+ } catch (Throwable e) {
+ Log.e(TAG, "Failure starting Accessibility Manager", e);
+ }
+
+ try {
Log.i(TAG, "Starting Notification Manager.");
ServiceManager.addService(Context.NOTIFICATION_SERVICE,
new NotificationManagerService(context, statusBar, hardware));
diff --git a/tests/FrameworkTest/AndroidManifest.xml b/tests/FrameworkTest/AndroidManifest.xml
index c70302b..4e4ebffc 100644
--- a/tests/FrameworkTest/AndroidManifest.xml
+++ b/tests/FrameworkTest/AndroidManifest.xml
@@ -20,6 +20,9 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.HARDWARE_TEST" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_VIEW_TYPES" />
+ <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_TRANSITION_TYPES" />
+ <uses-permission android:name="android.permission.ACCESSIBILITY_EVENT_NOTIFICATION_TYPES" />
<application android:theme="@style/Theme">
<uses-library android:name="android.test.runner" />
@@ -939,7 +942,7 @@
</intent-filter>
</activity>
- <activity android:name="android.widget.AutoCompleteTextViewSimple"
+ <activity android:name="android.widget.AutoCompleteTextViewSimple"
android:label="AutoCompleteTextViewSimple">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -947,6 +950,12 @@
</intent-filter>
</activity>
+ <service android:name=".accessibility.AccessibilityTestService">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService" />
+ </intent-filter>
+ </service>
+
</application>
</manifest>
diff --git a/tests/FrameworkTest/src/com/android/frameworktest/accessibility/AccessibilityTestService.java b/tests/FrameworkTest/src/com/android/frameworktest/accessibility/AccessibilityTestService.java
new file mode 100644
index 0000000..83d6056
--- /dev/null
+++ b/tests/FrameworkTest/src/com/android/frameworktest/accessibility/AccessibilityTestService.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2009 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.android.frameworktest.accessibility;
+
+import android.accessibilityservice.AccessibilityService;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Notification;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+import java.util.Timer;
+import java.util.TimerTask;
+
+/**
+ * This class text the accessibility framework end to end.
+ * <p>
+ * Note: Since accessibility is provided by {@link AccessibilityService}s we create one,
+ * and it generates an event and an interruption dispatching them through the
+ * {@link AccessibilityManager}. We verify the received result. To trigger the test
+ * go to Settings->Accessibility and select the enable accessibility check and then
+ * select the check for this service (same name as the class).
+ */
+public class AccessibilityTestService extends AccessibilityService {
+
+ private static final String LOG_TAG = "AccessibilityTestService";
+
+ private static final String CLASS_NAME = "foo.bar.baz.Test";
+ private static final String PACKAGE_NAME = "foo.bar.baz";
+ private static final String TEXT = "Some stuff";
+ private static final String BEFORE_TEXT = "Some other stuff";
+
+ private static final String CONTENT_DESCRIPTION = "Content description";
+
+ private static final int ITEM_COUNT = 10;
+ private static final int CURRENT_ITEM_INDEX = 1;
+ private static final int INTERRUPT_INVOCATION_TYPE = 0x00000200;
+
+ private static final int FROM_INDEX = 1;
+ private static final int ADDED_COUNT = 2;
+ private static final int REMOVED_COUNT = 1;
+
+ private static final int NOTIFICATION_TIMEOUT_MILLIS = 80;
+
+ private int mReceivedResult;
+
+ private Timer mTimer = new Timer();
+
+ @Override
+ public void onServiceConnected() {
+ AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+ info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+ info.feedbackType = AccessibilityServiceInfo.FEEDBACK_AUDIBLE;
+ info.notificationTimeout = NOTIFICATION_TIMEOUT_MILLIS;
+ info.flags &= AccessibilityServiceInfo.DEFAULT;
+ setServiceInfo(info);
+
+ // we need to wait until the system picks our configuration
+ // otherwise it will not notify us
+ mTimer.schedule(new TimerTask() {
+ @Override
+ public void run() {
+ try {
+ testAccessibilityEventDispatching();
+ testInterrupt();
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Error in testing Accessibility feature", e);
+ }
+ }
+ }, 1000);
+ }
+
+ /**
+ * Check here if the event we received is actually the one we sent.
+ */
+ @Override
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ assert(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED == event.getEventType());
+ assert(event != null);
+ assert(event.getEventTime() > 0);
+ assert(CLASS_NAME.equals(event.getClassName()));
+ assert(PACKAGE_NAME.equals(event.getPackageName()));
+ assert(1 == event.getText().size());
+ assert(TEXT.equals(event.getText().get(0)));
+ assert(BEFORE_TEXT.equals(event.getBeforeText()));
+ assert(event.isChecked());
+ assert(CONTENT_DESCRIPTION.equals(event.getContentDescription()));
+ assert(ITEM_COUNT == event.getItemCount());
+ assert(CURRENT_ITEM_INDEX == event.getCurrentItemIndex());
+ assert(event.isEnabled());
+ assert(event.isPassword());
+ assert(FROM_INDEX == event.getFromIndex());
+ assert(ADDED_COUNT == event.getAddedCount());
+ assert(REMOVED_COUNT == event.getRemovedCount());
+ assert(event.getParcelableData() != null);
+ assert(1 == ((Notification) event.getParcelableData()).icon);
+
+ // set the type of the receved request
+ mReceivedResult = AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED;
+ }
+
+ /**
+ * Set a flag that we received the interrupt request.
+ */
+ @Override
+ public void onInterrupt() {
+
+ // set the type of the receved request
+ mReceivedResult = INTERRUPT_INVOCATION_TYPE;
+ }
+
+ /**
+ * If an {@link AccessibilityEvent} is sent and received correctly.
+ */
+ public void testAccessibilityEventDispatching() throws Exception {
+ AccessibilityEvent event =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
+
+ assert(event != null);
+ event.setClassName(CLASS_NAME);
+ event.setPackageName(PACKAGE_NAME);
+ event.getText().add(TEXT);
+ event.setBeforeText(BEFORE_TEXT);
+ event.setChecked(true);
+ event.setContentDescription(CONTENT_DESCRIPTION);
+ event.setItemCount(ITEM_COUNT);
+ event.setCurrentItemIndex(CURRENT_ITEM_INDEX);
+ event.setEnabled(true);
+ event.setPassword(true);
+ event.setFromIndex(FROM_INDEX);
+ event.setAddedCount(ADDED_COUNT);
+ event.setRemovedCount(REMOVED_COUNT);
+ event.setParcelableData(new Notification(1, "Foo", 1234));
+
+ AccessibilityManager.getInstance(this).sendAccessibilityEvent(event);
+
+ assert(mReceivedResult == event.getEventType());
+
+ Log.i(LOG_TAG, "AccessibilityTestService#testAccessibilityEventDispatching: Success");
+ }
+
+ /**
+ * If accessibility feedback interruption is triggered and received correctly.
+ */
+ public void testInterrupt() throws Exception {
+ AccessibilityManager.getInstance(this).interrupt();
+
+ assert(INTERRUPT_INVOCATION_TYPE == mReceivedResult);
+
+ Log.i(LOG_TAG, "AccessibilityTestService#testInterrupt: Success");
+ }
+}
+
diff --git a/tests/FrameworkTest/tests/src/com/android/frameworktest/accessibility/RecycleAccessibilityEventTest.java b/tests/FrameworkTest/tests/src/com/android/frameworktest/accessibility/RecycleAccessibilityEventTest.java
new file mode 100644
index 0000000..d6380f9
--- /dev/null
+++ b/tests/FrameworkTest/tests/src/com/android/frameworktest/accessibility/RecycleAccessibilityEventTest.java
@@ -0,0 +1,81 @@
+/**
+ * Copyright (C) 2009 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.android.frameworktest.accessibility;
+
+import android.test.suitebuilder.annotation.MediumTest;
+import android.view.accessibility.AccessibilityEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * This class exercises the caching and recycling of {@link AccessibilityEvent}s.
+ */
+public class RecycleAccessibilityEventTest extends TestCase {
+
+ private static final String CLASS_NAME = "foo.bar.baz.Test";
+ private static final String PACKAGE_NAME = "foo.bar.baz";
+ private static final String TEXT = "Some stuff";
+
+ private static final String CONTENT_DESCRIPTION = "Content description";
+ private static final int ITEM_COUNT = 10;
+ private static final int CURRENT_ITEM_INDEX = 1;
+
+ private static final int FROM_INDEX = 1;
+ private static final int ADDED_COUNT = 2;
+ private static final int REMOVED_COUNT = 1;
+
+ /**
+ * If an {@link AccessibilityEvent} is marshaled/unmarshaled correctly
+ */
+ @MediumTest
+ public void testAccessibilityEventViewTextChangedType() {
+ AccessibilityEvent first =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);
+ assertNotNull(first);
+
+ first.setClassName(CLASS_NAME);
+ first.setPackageName(PACKAGE_NAME);
+ first.getText().add(TEXT);
+ first.setFromIndex(FROM_INDEX);
+ first.setAddedCount(ADDED_COUNT);
+ first.setRemovedCount(REMOVED_COUNT);
+ first.setChecked(true);
+ first.setContentDescription(CONTENT_DESCRIPTION);
+ first.setItemCount(ITEM_COUNT);
+ first.setCurrentItemIndex(CURRENT_ITEM_INDEX);
+ first.setEnabled(true);
+ first.setPassword(true);
+
+ first.recycle();
+
+ assertNotNull(first);
+ assertNull(first.getClassName());
+ assertNull(first.getPackageName());
+ assertEquals(0, first.getText().size());
+ assertFalse(first.isChecked());
+ assertNull(first.getContentDescription());
+ assertEquals(0, first.getItemCount());
+ assertEquals(AccessibilityEvent.INVALID_POSITION, first.getCurrentItemIndex());
+ assertFalse(first.isEnabled());
+ assertFalse(first.isPassword());
+ assertEquals(0, first.getFromIndex());
+ assertEquals(0, first.getAddedCount());
+ assertEquals(0, first.getRemovedCount());
+
+ // get another event from the pool (this must be the recycled first)
+ AccessibilityEvent second = AccessibilityEvent.obtain();
+ assertEquals(first, second);
+ }
+}