diff options
author | Svetoslav Ganov <svetoslavganov@google.com> | 2011-06-28 01:12:41 -0700 |
---|---|---|
committer | Svetoslav Ganov <svetoslavganov@google.com> | 2011-07-21 12:04:54 -0700 |
commit | 6179ea3196e9306d3f14361fe9ef14191b1edba6 (patch) | |
tree | d821da4d5840aebcddf4a714a3217ec595847bc9 | |
parent | ac4159549c10dbe428d42980278c0e43ecc8d93f (diff) | |
download | frameworks_base-6179ea3196e9306d3f14361fe9ef14191b1edba6.zip frameworks_base-6179ea3196e9306d3f14361fe9ef14191b1edba6.tar.gz frameworks_base-6179ea3196e9306d3f14361fe9ef14191b1edba6.tar.bz2 |
Adding accessibility support to the Status Bar.
1. Added content description to pretty much all animals
in the zoo including buttons in the navigation bar,
notifications and status icons for battery, signal,
data, etc.
2. Rectored to avoid ovelaying views since they block
touch exploratino. In general overlaying views
cause trouble for touch exploration and accessibility
in general.
3. Avoid sending accessibility events in case the user is
touching outside of the StatauBAr panels to avoid
confusion.
4. Added records to accessibility events in the places where
this would help the presentation. So the event comes from
a given "leaf" view and its predecessor is adding a record
to the event for itself to provide more cotext. It is up
to the accessiiblity service to choose how to present that.
bug:4686943
Change-Id: I1c1bd123d828fb10911bca92130e9a05c1f020b3
38 files changed, 732 insertions, 230 deletions
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 1af0983..ca64c88 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -97,9 +97,10 @@ public class StatusBarManager { } } - public void setIcon(String slot, int iconId, int iconLevel) { + public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) { try { - mService.setIcon(slot, mContext.getPackageName(), iconId, iconLevel); + mService.setIcon(slot, mContext.getPackageName(), iconId, iconLevel, + contentDescription); } catch (RemoteException ex) { // system process is dead anyway. throw new RuntimeException(ex); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 54fee3c..92a8ce7 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -2148,9 +2148,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager onPopulateAccessibilityEvent(event); // Let our children have a shot in populating the event. for (int i = 0, count = getChildCount(); i < count; i++) { - boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event); - if (handled) { - return handled; + View child = getChildAt(i); + if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) { + boolean handled = getChildAt(i).dispatchPopulateAccessibilityEvent(event); + if (handled) { + return handled; + } } } return false; diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 755d4e0..00c75a9 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -902,15 +902,16 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { @Override public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { - // Add a record for ourselves as well. - AccessibilityEvent record = AccessibilityEvent.obtain(); - record.setSource(this); - // Set the class since it is not populated in #dispatchPopulateAccessibilityEvent - record.setClassName(getClass().getName()); - child.onInitializeAccessibilityEvent(record); - child.dispatchPopulateAccessibilityEvent(record); - event.appendRecord(record); - return true; + if (super.onRequestSendAccessibilityEvent(child, event)) { + // Add a record for ourselves as well. + AccessibilityEvent record = AccessibilityEvent.obtain(); + onInitializeAccessibilityEvent(record); + // Populate with the text of the requesting child. + child.dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; + } + return false; } @Override diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 161b404..299e1ff 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -30,14 +30,15 @@ import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.RemotableViewMethod; import android.view.View; import android.view.ViewDebug; +import android.view.accessibility.AccessibilityEvent; import android.widget.RemoteViews.RemoteView; - /** * Displays an arbitrary image, such as an icon. The ImageView class * can load images from various sources (such as resources or content @@ -208,7 +209,15 @@ public class ImageView extends View { } return false; } - + + @Override + public void onPopulateAccessibilityEvent(AccessibilityEvent event) { + CharSequence contentDescription = getContentDescription(); + if (!TextUtils.isEmpty(contentDescription)) { + event.getText().add(contentDescription); + } + } + /** * Set this to true if you want the ImageView to adjust its bounds * to preserve the aspect ratio of its drawable. diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index a9e5057..07430e7 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -27,7 +27,7 @@ interface IStatusBarService void expand(); void collapse(); void disable(int what, IBinder token, String pkg); - void setIcon(String slot, String iconPackage, int iconId, int iconLevel); + void setIcon(String slot, String iconPackage, int iconId, int iconLevel, String contentDescription); void setIconVisibility(String slot, boolean visible); void removeIcon(String slot); void topAppWindowChanged(boolean menuVisible); diff --git a/core/java/com/android/internal/statusbar/StatusBarIcon.java b/core/java/com/android/internal/statusbar/StatusBarIcon.java index ae2cac2..3333c82 100644 --- a/core/java/com/android/internal/statusbar/StatusBarIcon.java +++ b/core/java/com/android/internal/statusbar/StatusBarIcon.java @@ -19,42 +19,35 @@ package com.android.internal.statusbar; import android.os.Parcel; import android.os.Parcelable; -/** - * @hide - */ public class StatusBarIcon implements Parcelable { public String iconPackage; public int iconId; public int iconLevel; public boolean visible = true; public int number; + public CharSequence contentDescription; - private StatusBarIcon() { - } - - public StatusBarIcon(String iconPackage, int iconId, int iconLevel) { - this.iconPackage = iconPackage; - this.iconId = iconId; - this.iconLevel = iconLevel; - } - - public StatusBarIcon(String iconPackage, int iconId, int iconLevel, int number) { + public StatusBarIcon(String iconPackage, int iconId, int iconLevel, int number, + CharSequence contentDescription) { this.iconPackage = iconPackage; this.iconId = iconId; this.iconLevel = iconLevel; this.number = number; + this.contentDescription = contentDescription; } + @Override public String toString() { return "StatusBarIcon(pkg=" + this.iconPackage + " id=0x" + Integer.toHexString(this.iconId) + " level=" + this.iconLevel + " visible=" + visible + " num=" + this.number + " )"; } + @Override public StatusBarIcon clone() { - StatusBarIcon that = new StatusBarIcon(this.iconPackage, this.iconId, this.iconLevel); + StatusBarIcon that = new StatusBarIcon(this.iconPackage, this.iconId, this.iconLevel, + this.number, this.contentDescription); that.visible = this.visible; - that.number = this.number; return that; } @@ -71,6 +64,7 @@ public class StatusBarIcon implements Parcelable { this.iconLevel = in.readInt(); this.visible = in.readInt() != 0; this.number = in.readInt(); + this.contentDescription = in.readCharSequence(); } public void writeToParcel(Parcel out, int flags) { @@ -79,6 +73,7 @@ public class StatusBarIcon implements Parcelable { out.writeInt(this.iconLevel); out.writeInt(this.visible ? 1 : 0); out.writeInt(this.number); + out.writeCharSequence(this.contentDescription); } public int describeContents() { diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2e870fe..509ee69 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3056,4 +3056,9 @@ <!-- Title for a dialog showing possible activities for sharing in ShareActionProvider [CHAR LIMIT=25] --> <string name="share_action_provider_share_with">Share with...</string> + <!-- Status Bar icon descriptions --> + + <!-- Description of for the status bar's icon that the device is locked for accessibility. [CHAR LIMIT=NONE] --> + <string name="status_bar_device_locked">Device locked.</string> + </resources> diff --git a/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml b/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml index d29e495..a354336 100644 --- a/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml +++ b/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml @@ -43,6 +43,7 @@ android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@drawable/compat_mode_help_diagram" + android:contentDescription="@string/accessibility_compatibility_zoom_example" /> <RelativeLayout android:orientation="horizontal" @@ -61,6 +62,7 @@ android:layout_alignParentRight="true" android:layout_centerVertical="true" android:src="@drawable/compat_mode_help_icon" + android:contentDescription="@string/accessibility_compatibility_zoom_button" /> <TextView android:id="@+id/explanation" diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml index d9f3f23..a2a6473 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml @@ -49,6 +49,7 @@ android:src="@drawable/ic_sysbar_back" android:layout_alignParentLeft="true" systemui:keyCode="4" + android:contentDescription="@string/accessibility_back" /> <LinearLayout android:id="@+id/navigationArea" @@ -62,11 +63,13 @@ android:layout_height="match_parent" android:src="@drawable/ic_sysbar_home" systemui:keyCode="3" + android:contentDescription="@string/accessibility_home" /> <ImageView android:id="@+id/recent_apps" android:layout_width="80dip" android:layout_height="match_parent" android:src="@drawable/ic_sysbar_recent" + android:contentDescription="@string/accessibility_menu" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu" android:layout_width="80dip" @@ -74,6 +77,7 @@ android:src="@drawable/ic_sysbar_menu" systemui:keyCode="82" android:visibility="invisible" + android:contentDescription="@string/accessibility_menu" /> </LinearLayout> diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml index 3fef7e0..41a20fb 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_input_methods_item.xml @@ -53,7 +53,8 @@ android:id="@+id/item_icon" android:layout_width="@android:dimen/app_icon_size" android:layout_height="wrap_content" - android:scaleType="fitCenter" /> + android:scaleType="fitCenter" + android:contentDescription="@null" /> <LinearLayout android:orientation="vertical" android:layout_width="0px" @@ -94,7 +95,8 @@ android:visibility="visible" android:clickable="true" android:focusable="true" - android:background="?android:attr/selectableItemBackground" /> + android:background="?android:attr/selectableItemBackground" + android:contentDescription="@string/accessibility_settings_button" /> </LinearLayout> <View android:layout_width="match_parent" diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml index fecfe7f..1e3099d 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml @@ -16,7 +16,7 @@ --> <!-- notification icons & panel access --> -<LinearLayout +<com.android.systemui.statusbar.tablet.NotificationArea xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/notificationArea" @@ -40,6 +40,7 @@ android:layout_marginLeft="8dip" android:src="@drawable/ic_sysbar_ime_default" android:visibility="gone" + android:contentDescription="@string/accessibility_ime_switch_button" /> <com.android.systemui.statusbar.policy.CompatModeButton @@ -49,6 +50,7 @@ android:layout_marginLeft="8dip" android:src="@drawable/ic_sysbar_zoom" android:visibility="gone" + android:contentDescription="@string/accessibility_compatibility_zoom_button" /> <com.android.systemui.statusbar.tablet.NotificationIconArea @@ -152,4 +154,4 @@ /> </LinearLayout> </LinearLayout> -</LinearLayout> +</com.android.systemui.statusbar.tablet.NotificationArea> diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml index 543f4ed..bbb2bc6 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel_title.xml @@ -14,15 +14,17 @@ limitations under the License. --> -<RelativeLayout +<com.android.systemui.statusbar.tablet.NotificationPanelTitle xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/title_area" - android:layout_width="0dp" - android:layout_height="0dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" android:orientation="vertical" android:background="@drawable/notify_panel_clock_bg" > + <LinearLayout android:id="@+id/icons" android:layout_width="wrap_content" @@ -34,6 +36,7 @@ android:layout_marginTop="16dp" android:layout_marginBottom="16dp" > + <ImageView android:id="@+id/bluetooth" android:layout_height="32dp" @@ -41,6 +44,7 @@ android:scaleType="centerInside" android:baseline="22dp" android:visibility="gone" + android:contentDescription="@null" /> <FrameLayout @@ -49,21 +53,28 @@ android:layout_width="32dp" android:layout_marginRight="4dp" > + <ImageView android:id="@+id/network_signal" android:layout_height="match_parent" android:layout_width="match_parent" + android:contentDescription="@null" /> + <ImageView android:id="@+id/network_type" android:layout_height="match_parent" android:layout_width="match_parent" + android:contentDescription="@null" /> + <ImageView android:id="@+id/network_direction" android:layout_height="match_parent" android:layout_width="match_parent" + android:contentDescription="@null" /> + </FrameLayout> <TextView @@ -86,6 +97,7 @@ android:layout_toRightOf="@id/network_text" android:layout_alignBaseline="@id/network_signal" android:baseline="22dp" + android:contentDescription="@null" /> <TextView @@ -110,6 +122,7 @@ android:paddingRight="16dp" android:src="@drawable/ic_sysbar_quicksettings" android:baseline="21dp" + android:contentDescription="@string/accessibility_settings_button" /> <ImageView @@ -122,6 +135,7 @@ android:src="@drawable/ic_notification_open" android:baseline="21dp" android:visibility="invisible" + android:contentDescription="@string/accessibility_notifications_button" /> <View @@ -138,7 +152,7 @@ <com.android.systemui.statusbar.tablet.HoloClock android:id="@+id/clock" android:layout_height="wrap_content" - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_alignParentRight="true" android:layout_above="@id/title_divider" android:layout_marginRight="6dip" @@ -164,19 +178,11 @@ android:id="@+id/date" style="@style/StatusBarNotificationText" android:layout_height="wrap_content" - android:layout_width="120dp" + android:layout_width="wrap_content" android:layout_alignBottom="@id/clock" android:layout_alignParentLeft="true" android:gravity="left" android:layout_marginLeft="32dp" /> - <view - class="com.android.systemui.statusbar.tablet.NotificationPanel$ModeToggle" - android:id="@+id/mode_toggle" - android:background="@null" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:clickable="true" - /> -</RelativeLayout> +</com.android.systemui.statusbar.tablet.NotificationPanelTitle> diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml index 51e7d97..5d7e8de 100644 --- a/packages/SystemUI/res/layout/navigation_bar.xml +++ b/packages/SystemUI/res/layout/navigation_bar.xml @@ -54,6 +54,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" + android:contentDescription="@string/accessibility_back" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" android:layout_width="80dp" @@ -66,6 +67,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1" + android:contentDescription="@string/accessibility_home" /> <ImageView android:id="@+id/recent_apps" android:layout_width="80dp" @@ -80,6 +82,7 @@ systemui:keyCode="82" android:layout_weight="0" android:visibility="invisible" + android:contentDescription="@string/accessibility_menu" /> </LinearLayout> @@ -124,6 +127,7 @@ android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="1" + android:contentDescription="@string/accessibility_menu" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home" android:layout_height="80dp" @@ -131,11 +135,13 @@ android:src="@drawable/ic_sysbar_home_default_land" systemui:keyCode="3" android:layout_weight="0" + android:contentDescription="@string/accessibility_home" /> <View android:layout_height="match_parent" android:layout_width="match_parent" android:layout_weight="1" + android:contentDescription="@string/accessibility_back" /> <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back" android:layout_height="80dp" @@ -148,6 +154,7 @@ android:layout_height="40dp" android:layout_width="match_parent" android:layout_weight="0" + android:contentDescription="@string/accessibility_menu" /> </LinearLayout> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 01cf2dc..082dab3 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -169,4 +169,135 @@ <string name="screenshot_saving_toast">Screenshot saved to Gallery</string> <!-- toast message displayed when we fail to take a screenshot. --> <string name="screenshot_failed_toast">Could not save screenshot</string> + + <!-- Title for the USB function chooser in UsbPreferenceActivity. [CHAR LIMIT=30] --> + <string name="usb_preference_title">USB file transfer options</string> + <!-- Label for the MTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] --> + <string name="use_mtp_button_title">Mount as a media player (MTP)</string> + <!-- Label for the PTP USB function in UsbPreferenceActivity. [CHAR LIMIT=50] --> + <string name="use_ptp_button_title">Mount as a camera (PTP)</string> + <!-- Label for the installer CD image option in UsbPreferenceActivity. [CHAR LIMIT=50] --> + <string name="installer_cd_button_title">Install Android File Transfer application for Mac</string> + + <!-- Content description of the back button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_back">Back</string> + <!-- Content description of the home button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_home">Home</string> + <!-- Content description of the menu button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_menu">Menu</string> + + <!-- Content description of the switch input method button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_ime_switch_button">Switch input method button.</string> + <!-- Content description of the compatibility zoom button for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_compatibility_zoom_button">Compatibility zoom button.</string> + + <!-- Content description of picture of the compatibility zoom example for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_compatibility_zoom_example">Zoom smaller to larger screen.</string> + + <!-- Content description of the bluetooth icon when connected for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_bluetooth_connected">Bluetooth connected.</string> + <!-- Content description of the bluetooth icon when connecting for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_bluetooth_disconnected">Bluetooth disconnected.</string> + + <!-- Content description of the battery when no battery for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_no_battery">No battery.</string> + <!-- Content description of the battery when it is one bar for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_one_bar">Battery one bar.</string> + <!-- Content description of the battery when it is two bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_two_bars">Battery two bars.</string> + <!-- Content description of the battery when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_three_bars">Battery three bars.</string> + <!-- Content description of the battery when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_full">Battery full.</string> + + <!-- Content description of the phone signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_no_phone">No phone.</string> + <!-- Content description of the phone signal when it is one bar for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_phone_one_bar">Phone one bar.</string> + <!-- Content description of the phone signal when it is two bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_phone_two_bars">Phone two bars.</string> + <!-- Content description of the phone signal when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_phone_three_bars">Phone three bars.</string> + <!-- Content description of the phone signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_phone_signal_full">Phone signal full.</string> + + <!-- Content description of the data signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_no_data">No data.</string> + <!-- Content description of the data signal when it is one bar for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_one_bar">Data one bar.</string> + <!-- Content description of the data signal when it is two bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_two_bars">Data two bars.</string> + <!-- Content description of the data signal when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_three_bars">Data three bars.</string> + <!-- Content description of the data signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_signal_full">Data signal full.</string> + + <!-- Content description of the WIFI signal when no signal for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_no_wifi">No WiFi.</string> + <!-- Content description of the WIFI signal when it is one bar for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_wifi_one_bar">WiFi one bar.</string> + <!-- Content description of the WIFI signal when it is two bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_wifi_two_bars">WiFi two bars.</string> + <!-- Content description of the WIFI signal when it is three bars for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_wifi_three_bars">WiFi three bars.</string> + <!-- Content description of the WIFI signal when it is full for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_wifi_signal_full">WiFi signal full.</string> + + <!-- Content description of the data connection type GPRS for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_gprs">GPRS</string> + + <!-- Content description of the data connection type 3G for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_3g">3G</string> + + <!-- Content description of the data connection type 3.5G for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_3.5g">3.5G</string> + + <!-- Content description of the data connection type 4G for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_4g">4G</string> + + <!-- Content description of the data connection type CDMA for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_cdma">CDMA</string> + + <!-- Content description of the data connection type Edge for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_edge">Edge</string> + + <!-- Content description of the data connection type WiFi for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_data_connection_wifi">WiFi</string> + + <!-- Content description of the data connection with no SIM for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_no_sim">No SIM.</string> + + <!-- Content description of the bluetooth tethering icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_bluetooth_tether">Bluetooth tethering.</string> + + <!-- Content description of the airplane mode icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_airplane_mode">Airplane mode.</string> + + <!-- Content description of the battery level icon for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_battery_level">Battery <xliff:g id="number">%d</xliff:g> percent.</string> + + <!-- Content description of the button for showing a settings panel in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_settings_button">Settings button.</string> + + <!-- Content description of the button for showing a notifications panel in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_notifications_button">Notifications button.</string> + + <!-- Content description of the button for removing a notification in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_remove_notification">Remove notification.</string> + + <!-- Content description of the enabled GPS icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_gps_enabled">GPS enabled.</string> + + <!-- Content description of the acquiring GPS icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_gps_acquiring">GPS acquiring.</string> + + <!-- Content description of the TeleTypewriter(TTY) enabled icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_tty_enabled">TeleTypewriter enabled.</string> + + <!-- Content description of the ringer vibrate icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_ringer_vibrate">Ringer vibrate.</string> + + <!-- Content description of the ringer silent icon in the notification panel for accessibility (not shown on the screen). [CHAR LIMIT=NONE] --> + <string name="accessibility_ringer_silent">Ringer silent.</string> + </resources> diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index bc0a508..cca7947 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -22,7 +22,6 @@ import java.util.List; import android.animation.Animator; import android.animation.LayoutTransition; import android.app.ActivityManager; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -48,6 +47,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; @@ -112,7 +112,7 @@ public class RecentsPanelView extends RelativeLayout position = _pos; packageName = _packageName; } - }; + } private final class OnLongClickDelegate implements View.OnLongClickListener { View mOtherView; @@ -252,6 +252,19 @@ public class RecentsPanelView extends RelativeLayout mChoreo.setPanelHeight(mRecentsContainer.getHeight()); } + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Ignore hover events outside of this panel bounds since such events + // generate spurious accessibility events with the panel content when + // tapping outside of it, thus confusing the user. + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { + return super.dispatchHoverEvent(event); + } + return true; + } + /** * Whether the panel is showing, or, if it's animating, whether it will be * when the animation is done. @@ -316,7 +329,6 @@ public class RecentsPanelView extends RelativeLayout mRecentsGlowView = findViewById(R.id.recents_glow); - mRecentsScrim = (View) findViewById(R.id.recents_bg_protect); mChoreo = new Choreographer(this, mRecentsScrim, mRecentsGlowView, this); mRecentsDismissButton = findViewById(R.id.recents_dismiss_button); mRecentsDismissButton.setOnClickListener(new OnClickListener() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java index 64ec063..6419777 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LatestItemView.java @@ -18,8 +18,8 @@ package com.android.systemui.statusbar; import android.content.Context; import android.util.AttributeSet; -import android.util.Slog; -import android.view.MotionEvent; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; public class LatestItemView extends FrameLayout { @@ -27,7 +27,22 @@ public class LatestItemView extends FrameLayout { super(context, attrs); } + @Override public void setOnClickListener(OnClickListener l) { super.setOnClickListener(l); } + + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (super.onRequestSendAccessibilityEvent(child, event)) { + // Add a record for the entire layout since its content is somehow small. + // The event comes from a leaf view that is interacted with. + AccessibilityEvent record = AccessibilityEvent.obtain(); + onInitializeAccessibilityEvent(record); + dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; + } + return false; + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index d9d9c06..be4b395 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -16,6 +16,7 @@ package com.android.systemui.statusbar; +import android.app.Notification; import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; @@ -23,11 +24,11 @@ import android.graphics.drawable.Drawable; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; +import android.text.TextUtils; import android.util.Slog; import android.util.Log; -import android.view.View; import android.view.ViewDebug; -import android.widget.FrameLayout; +import android.view.accessibility.AccessibilityEvent; import java.text.NumberFormat; @@ -45,8 +46,9 @@ public class StatusBarIconView extends AnimatedImageView { private int mNumberX; private int mNumberY; private String mNumberText; + private Notification mNotification; - public StatusBarIconView(Context context, String slot) { + public StatusBarIconView(Context context, String slot, Notification notification) { super(context); final Resources res = context.getResources(); mSlot = slot; @@ -54,6 +56,8 @@ public class StatusBarIconView extends AnimatedImageView { mNumberPain.setTextAlign(Paint.Align.CENTER); mNumberPain.setColor(res.getColor(R.drawable.notification_number_text_color)); mNumberPain.setAntiAlias(true); + mNotification = notification; + setContentDescription(notification); } private static boolean streq(String a, String b) { @@ -83,6 +87,7 @@ public class StatusBarIconView extends AnimatedImageView { final boolean numberEquals = mIcon != null && mIcon.number == icon.number; mIcon = icon.clone(); + setContentDescription(icon.contentDescription); if (!iconEquals) { Drawable drawable = getIcon(icon); if (drawable == null) { @@ -159,6 +164,15 @@ public class StatusBarIconView extends AnimatedImageView { return mIcon; } + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (mNotification != null) { + event.setParcelableData(mNotification); + } + } + + @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (mNumberBackground != null) { @@ -166,6 +180,7 @@ public class StatusBarIconView extends AnimatedImageView { } } + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -175,6 +190,7 @@ public class StatusBarIconView extends AnimatedImageView { } } + @Override protected void debug(int depth) { super.debug(depth); Log.d("View", debugIndent(depth) + "slot=" + mSlot); @@ -213,4 +229,13 @@ public class StatusBarIconView extends AnimatedImageView { mNumberY = h-r.bottom-((dh-r.top-th-r.bottom)/2); mNumberBackground.setBounds(w-dw, h-dh, w, h); } + + private void setContentDescription(Notification notification) { + if (notification != null) { + CharSequence tickerText = notification.tickerText; + if (!TextUtils.isEmpty(tickerText)) { + setContentDescription(tickerText); + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java index e1d17a8..f6aa159 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/IconMerger.java @@ -33,7 +33,8 @@ public class IconMerger extends LinearLayout { private int mIconSize; private StatusBarIconView mMoreView; - private StatusBarIcon mMoreIcon = new StatusBarIcon(null, R.drawable.stat_notify_more, 0); + private StatusBarIcon mMoreIcon = new StatusBarIcon(null, R.drawable.stat_notify_more, 0, 0, + null); public IconMerger(Context context, AttributeSet attrs) { super(context, attrs); @@ -41,7 +42,7 @@ public class IconMerger extends LinearLayout { mIconSize = context.getResources().getDimensionPixelSize( com.android.internal.R.dimen.status_bar_icon_size); - mMoreView = new StatusBarIconView(context, "more"); + mMoreView = new StatusBarIconView(context, "more", null); mMoreView.set(mMoreIcon); addView(mMoreView, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize)); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index f4c4bbc..b93ad68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -472,7 +472,7 @@ public class PhoneStatusBar extends StatusBar { public void addIcon(String slot, int index, int viewIndex, StatusBarIcon icon) { if (SPEW) Slog.d(TAG, "addIcon slot=" + slot + " index=" + index + " viewIndex=" + viewIndex + " icon=" + icon); - StatusBarIconView view = new StatusBarIconView(mContext, slot); + StatusBarIconView view = new StatusBarIconView(mContext, slot, null); view.set(icon); mStatusIcons.addView(view, viewIndex, new LinearLayout.LayoutParams(mIconSize, mIconSize)); } @@ -607,7 +607,7 @@ public class PhoneStatusBar extends StatusBar { // Update the icon. final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, notification.notification.iconLevel, - notification.notification.number); + notification.notification.number, notification.notification.tickerText); if (!oldEntry.icon.set(ic)) { handleNotificationError(key, notification, "Couldn't update icon: " + ic); return; @@ -765,9 +765,11 @@ public class PhoneStatusBar extends StatusBar { final View expanded = views[2]; // Construct the icon. final StatusBarIconView iconView = new StatusBarIconView(mContext, - notification.pkg + "/0x" + Integer.toHexString(notification.id)); + notification.pkg + "/0x" + Integer.toHexString(notification.id), + notification.notification); final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, - notification.notification.iconLevel, notification.notification.number); + notification.notification.iconLevel, notification.notification.number, + notification.notification.tickerText); if (!iconView.set(ic)) { handleNotificationError(key, notification, "Coulding create icon: " + ic); return null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index af5c72d..7b50985 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -18,25 +18,17 @@ package com.android.systemui.statusbar.phone; import android.app.StatusBarManager; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothProfile; -import android.bluetooth.BluetoothPbap; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.content.res.TypedArray; -import android.graphics.PixelFormat; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; import android.location.LocationManager; import android.media.AudioManager; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.Uri; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Handler; -import android.os.Message; import android.os.RemoteException; import android.os.storage.StorageManager; import android.provider.Settings; @@ -44,18 +36,7 @@ import android.telephony.PhoneStateListener; import android.telephony.ServiceState; import android.telephony.SignalStrength; import android.telephony.TelephonyManager; -import android.text.format.DateFormat; -import android.text.style.CharacterStyle; -import android.text.style.RelativeSizeSpan; -import android.text.style.ForegroundColorSpan; -import android.text.style.StyleSpan; -import android.text.Spannable; -import android.text.SpannableStringBuilder; import android.util.Slog; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.LinearLayout; import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.IccCard; @@ -63,7 +44,6 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.internal.telephony.cdma.EriInfo; import com.android.internal.telephony.cdma.TtyIntent; import com.android.server.am.BatteryStatsService; - import com.android.systemui.R; /** @@ -447,6 +427,32 @@ public class PhoneStatusBarPolicy { R.drawable.stat_sys_data_fully_inandout_1x } }; + // Accessibility; + + private static final int[] sPhoneSignalStrength = { + R.string.accessibility_no_phone, + R.string.accessibility_phone_one_bar, + R.string.accessibility_phone_two_bars, + R.string.accessibility_phone_three_bars, + R.string.accessibility_phone_signal_full + }; + + private static final int[] sDataConnectionStrength = { + R.string.accessibility_no_data, + R.string.accessibility_data_one_bar, + R.string.accessibility_data_two_bars, + R.string.accessibility_data_three_bars, + R.string.accessibility_data_signal_full + }; + + private static final int[] sWifiConnectionStrength = { + R.string.accessibility_no_wifi, + R.string.accessibility_wifi_one_bar, + R.string.accessibility_wifi_two_bars, + R.string.accessibility_wifi_three_bars, + R.string.accessibility_wifi_signal_full + }; + // Assume it's all good unless we hear otherwise. We don't always seem // to get broadcasts that it *is* there. IccCard.State mSimState = IccCard.State.READY; @@ -546,12 +552,13 @@ public class PhoneStatusBarPolicy { new com.android.systemui.usb.StorageNotification(context)); // battery - mService.setIcon("battery", com.android.internal.R.drawable.stat_sys_battery_unknown, 0); + mService.setIcon("battery", com.android.internal.R.drawable.stat_sys_battery_unknown, 0, + null); // phone_signal mPhone = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); mPhoneSignalIconId = R.drawable.stat_sys_signal_null; - mService.setIcon("phone_signal", mPhoneSignalIconId, 0); + mService.setIcon("phone_signal", mPhoneSignalIconId, 0, null); // register for phone state notifications. ((TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE)) @@ -563,24 +570,24 @@ public class PhoneStatusBarPolicy { | PhoneStateListener.LISTEN_DATA_ACTIVITY); // data_connection - mService.setIcon("data_connection", R.drawable.stat_sys_data_connected_g, 0); + mService.setIcon("data_connection", R.drawable.stat_sys_data_connected_g, 0, null); mService.setIconVisibility("data_connection", false); // wifi - mService.setIcon("wifi", sWifiSignalImages[0][0], 0); + mService.setIcon("wifi", sWifiSignalImages[0][0], 0, null); mService.setIconVisibility("wifi", false); // wifi will get updated by the sticky intents // TTY status - mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0); + mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0, null); mService.setIconVisibility("tty", false); // Cdma Roaming Indicator, ERI - mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_0, 0); + mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_0, 0, null); mService.setIconVisibility("cdma_eri", false); // bluetooth status - mService.setIcon("bluetooth", R.drawable.stat_sys_data_bluetooth, 0); + mService.setIcon("bluetooth", R.drawable.stat_sys_data_bluetooth, 0, null); BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); if (adapter != null) { mBluetoothEnabled = adapter.isEnabled(); @@ -590,21 +597,23 @@ public class PhoneStatusBarPolicy { mService.setIconVisibility("bluetooth", mBluetoothEnabled); // Gps status - mService.setIcon("gps", R.drawable.stat_sys_gps_acquiring_anim, 0); + mService.setIcon("gps", R.drawable.stat_sys_gps_acquiring_anim, 0, null); mService.setIconVisibility("gps", false); // Alarm clock - mService.setIcon("alarm_clock", R.drawable.stat_notify_alarm, 0); + mService.setIcon("alarm_clock", R.drawable.stat_notify_alarm, 0, null); mService.setIconVisibility("alarm_clock", false); // Sync state - mService.setIcon("sync_active", com.android.internal.R.drawable.stat_notify_sync_anim0, 0); - mService.setIcon("sync_failing", com.android.internal.R.drawable.stat_notify_sync_error, 0); + mService.setIcon("sync_active", com.android.internal.R.drawable.stat_notify_sync_anim0, + 0, null); + mService.setIcon("sync_failing", com.android.internal.R.drawable.stat_notify_sync_error, + 0, null); mService.setIconVisibility("sync_active", false); mService.setIconVisibility("sync_failing", false); // volume - mService.setIcon("volume", R.drawable.stat_sys_ringer_silent, 0); + mService.setIcon("volume", R.drawable.stat_sys_ringer_silent, 0, null); mService.setIconVisibility("volume", false); updateVolume(); @@ -655,7 +664,8 @@ public class PhoneStatusBarPolicy { private final void updateBattery(Intent intent) { final int id = intent.getIntExtra("icon-small", 0); int level = intent.getIntExtra("level", 0); - mService.setIcon("battery", id, level); + String contentDescription = mContext.getString(R.string.accessibility_battery_level, level); + mService.setIcon("battery", id, level, contentDescription); } private void updateConnectivity(Intent intent) { @@ -677,12 +687,16 @@ public class PhoneStatusBarPolicy { if (info.isConnected()) { mIsWifiConnected = true; int iconId; + String contentDescription = null; if (mLastWifiSignalLevel == -1) { iconId = sWifiSignalImages[mInetCondition][0]; + contentDescription = mContext.getString(sWifiConnectionStrength[0]); } else { iconId = sWifiSignalImages[mInetCondition][mLastWifiSignalLevel]; - } - mService.setIcon("wifi", iconId, 0); + contentDescription = mContext.getString( + sWifiConnectionStrength[mLastWifiSignalLevel]); + } + mService.setIcon("wifi", iconId, 0, contentDescription); // Show the icon since wi-fi is connected mService.setIconVisibility("wifi", true); } else { @@ -690,7 +704,8 @@ public class PhoneStatusBarPolicy { mIsWifiConnected = false; int iconId = sWifiSignalImages[0][0]; - mService.setIcon("wifi", iconId, 0); + String contentDescription = mContext.getString(R.string.accessibility_no_wifi); + mService.setIcon("wifi", iconId, 0, contentDescription); // Hide the icon since we're not connected mService.setIconVisibility("wifi", false); } @@ -781,6 +796,7 @@ public class PhoneStatusBarPolicy { private final void updateSignalStrength() { int[] iconList; + String contentDescription = null; // Display signal strength while in "emergency calls only" mode if (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly())) { @@ -788,10 +804,12 @@ public class PhoneStatusBarPolicy { if (Settings.System.getInt(mContext.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1) { mPhoneSignalIconId = R.drawable.stat_sys_signal_flightmode; + contentDescription = mContext.getString(R.string.accessibility_airplane_mode); } else { mPhoneSignalIconId = R.drawable.stat_sys_signal_null; + contentDescription = mContext.getString(R.string.accessibility_no_phone); } - mService.setIcon("phone_signal", mPhoneSignalIconId, 0); + mService.setIcon("phone_signal", mPhoneSignalIconId, 0, contentDescription); return; } @@ -805,8 +823,11 @@ public class PhoneStatusBarPolicy { } else { iconList = sSignalImages[mInetCondition]; } - mPhoneSignalIconId = iconList[mSignalStrength.getLevel()]; - mService.setIcon("phone_signal", mPhoneSignalIconId, 0); + + final int signalLevel = mSignalStrength.getLevel(); + mPhoneSignalIconId = iconList[signalLevel]; + contentDescription = mContext.getString(sPhoneSignalStrength[signalLevel]); + mService.setIcon("phone_signal", mPhoneSignalIconId, 0, contentDescription); } private final void updateDataNetType(int net) { @@ -850,6 +871,7 @@ public class PhoneStatusBarPolicy { private final void updateDataIcon() { int iconId; + String contentDescription = null; boolean visible = true; if (!isCdma()) { @@ -870,13 +892,15 @@ public class PhoneStatusBarPolicy { iconId = mDataIconList[0]; break; } - mService.setIcon("data_connection", iconId, 0); + contentDescription = mContext.getString(sDataConnectionStrength[mDataActivity]); + mService.setIcon("data_connection", iconId, 0, contentDescription); } else { visible = false; } } else { iconId = R.drawable.stat_sys_no_sim; - mService.setIcon("data_connection", iconId, 0); + contentDescription = mContext.getString(R.string.accessibility_no_sim); + mService.setIcon("data_connection", iconId, 0, contentDescription); } } else { // CDMA case, mDataActivity can be also DATA_ACTIVITY_DORMANT @@ -896,7 +920,7 @@ public class PhoneStatusBarPolicy { iconId = mDataIconList[0]; break; } - mService.setIcon("data_connection", iconId, 0); + mService.setIcon("data_connection", iconId, 0, null); } else { visible = false; } @@ -921,12 +945,19 @@ public class PhoneStatusBarPolicy { final int ringerMode = audioManager.getRingerMode(); final boolean visible = ringerMode == AudioManager.RINGER_MODE_SILENT || ringerMode == AudioManager.RINGER_MODE_VIBRATE; - final int iconId = audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER) - ? R.drawable.stat_sys_ringer_vibrate - : R.drawable.stat_sys_ringer_silent; + + final int iconId; + String contentDescription = null; + if (audioManager.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) { + iconId = R.drawable.stat_sys_ringer_vibrate; + contentDescription = mContext.getString(R.string.accessibility_ringer_vibrate); + } else { + iconId = R.drawable.stat_sys_ringer_silent; + contentDescription = mContext.getString(R.string.accessibility_ringer_silent); + } if (visible) { - mService.setIcon("volume", iconId, 0); + mService.setIcon("volume", iconId, 0, contentDescription); } if (visible != mVolumeVisible) { mService.setIconVisibility("volume", visible); @@ -936,6 +967,7 @@ public class PhoneStatusBarPolicy { private final void updateBluetooth(Intent intent) { int iconId = R.drawable.stat_sys_data_bluetooth; + String contentDescription = null; String action = intent.getAction(); if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); @@ -945,12 +977,16 @@ public class PhoneStatusBarPolicy { BluetoothAdapter.STATE_DISCONNECTED); if (state == BluetoothAdapter.STATE_CONNECTED) { iconId = R.drawable.stat_sys_data_bluetooth_connected; + contentDescription = mContext.getString(R.string.accessibility_bluetooth_connected); + } else { + contentDescription = mContext.getString( + R.string.accessibility_bluetooth_disconnected); } } else { return; } - mService.setIcon("bluetooth", iconId, 0); + mService.setIcon("bluetooth", iconId, 0, contentDescription); mService.setIconVisibility("bluetooth", mBluetoothEnabled); } @@ -974,6 +1010,7 @@ public class PhoneStatusBarPolicy { } } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) { int iconId; + String contentDescription = null; final int newRssi = intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200); int newSignalLevel = WifiManager.calculateSignalLevel(newRssi, sWifiSignalImages[0].length); @@ -981,10 +1018,13 @@ public class PhoneStatusBarPolicy { mLastWifiSignalLevel = newSignalLevel; if (mIsWifiConnected) { iconId = sWifiSignalImages[mInetCondition][newSignalLevel]; + contentDescription = mContext.getString( + sWifiConnectionStrength[newSignalLevel]); } else { iconId = sWifiTemporarilyNotConnectedImage; + contentDescription = mContext.getString(R.string.accessibility_no_wifi); } - mService.setIcon("wifi", iconId, 0); + mService.setIcon("wifi", iconId, 0, contentDescription); } } } @@ -995,14 +1035,16 @@ public class PhoneStatusBarPolicy { if (action.equals(LocationManager.GPS_FIX_CHANGE_ACTION) && enabled) { // GPS is getting fixes - mService.setIcon("gps", com.android.internal.R.drawable.stat_sys_gps_on, 0); + mService.setIcon("gps", com.android.internal.R.drawable.stat_sys_gps_on, 0, + mContext.getString(R.string.accessibility_gps_enabled)); mService.setIconVisibility("gps", true); } else if (action.equals(LocationManager.GPS_ENABLED_CHANGE_ACTION) && !enabled) { // GPS is off mService.setIconVisibility("gps", false); } else { // GPS is on, but not receiving fixes - mService.setIcon("gps", R.drawable.stat_sys_gps_acquiring_anim, 0); + mService.setIcon("gps", R.drawable.stat_sys_gps_acquiring_anim, 0, + mContext.getString(R.string.accessibility_gps_acquiring)); mService.setIconVisibility("gps", true); } } @@ -1016,7 +1058,8 @@ public class PhoneStatusBarPolicy { if (enabled) { // TTY is on if (false) Slog.v(TAG, "updateTTY: set TTY on"); - mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0); + mService.setIcon("tty", R.drawable.stat_sys_tty_mode, 0, + mContext.getString(R.string.accessibility_tty_enabled)); mService.setIconVisibility("tty", true); } else { // TTY is off @@ -1058,15 +1101,15 @@ public class PhoneStatusBarPolicy { switch (iconMode) { case EriInfo.ROAMING_ICON_MODE_NORMAL: - mService.setIcon("cdma_eri", iconList[iconIndex], 0); + mService.setIcon("cdma_eri", iconList[iconIndex], 0, null); mService.setIconVisibility("cdma_eri", true); break; case EriInfo.ROAMING_ICON_MODE_FLASH: - mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_flash, 0); + mService.setIcon("cdma_eri", R.drawable.stat_sys_roaming_cdma_flash, 0, null); mService.setIconVisibility("cdma_eri", true); break; } - mService.setIcon("phone_signal", mPhoneSignalIconId, 0); + mService.setIcon("phone_signal", mPhoneSignalIconId, 0, null); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java index 84c524a..c2390e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java @@ -26,6 +26,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; +import android.view.accessibility.AccessibilityEvent; import android.widget.FrameLayout; import com.android.systemui.R; @@ -203,5 +204,19 @@ public class PhoneStatusBarView extends FrameLayout { return mService.interceptTouchEvent(event) ? true : super.onInterceptTouchEvent(event); } -} + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (super.onRequestSendAccessibilityEvent(child, event)) { + // The status bar is very small so augment the view that the user is touching + // with the content of the status bar a whole. This way an accessibility service + // may announce the current item as well as the entire content if appropriate. + AccessibilityEvent record = AccessibilityEvent.obtain(); + onInitializeAccessibilityEvent(record); + dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java index 8ee12de..e76fe51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java @@ -183,7 +183,8 @@ public abstract class Ticker { } final Drawable icon = StatusBarIconView.getIcon(mContext, - new StatusBarIcon(n.pkg, n.notification.icon, n.notification.iconLevel, 0)); + new StatusBarIcon(n.pkg, n.notification.icon, n.notification.iconLevel, 0, + n.notification.tickerText)); final Segment newSegment = new Segment(n, icon, n.notification.tickerText); // If there's already a notification schedule for this package and id, remove it. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java new file mode 100644 index 0000000..13fb03e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityContentDescriptions.java @@ -0,0 +1,37 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package com.android.systemui.statusbar.policy; + +import com.android.systemui.R; + +/** + * Content descriptions for accessibility support. + */ +public class AccessibilityContentDescriptions { + + private AccessibilityContentDescriptions() {} + + static final int[] PHONE_SIGNAL_STRENGTH = { + R.string.accessibility_no_phone, + R.string.accessibility_phone_one_bar, + R.string.accessibility_phone_two_bars, + R.string.accessibility_phone_three_bars, + R.string.accessibility_phone_signal_full + }; + + static final int[] DATA_CONNECTION_STRENGTH = { + R.string.accessibility_no_data, + R.string.accessibility_data_one_bar, + R.string.accessibility_data_two_bars, + R.string.accessibility_data_three_bars, + R.string.accessibility_data_signal_full + }; + + static final int[] WIFI_CONNECTION_STRENGTH = { + R.string.accessibility_no_wifi, + R.string.accessibility_wifi_one_bar, + R.string.accessibility_wifi_two_bars, + R.string.accessibility_wifi_three_bars, + R.string.accessibility_wifi_signal_full + }; +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java index ae2b6b2..3957c1b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java @@ -62,12 +62,15 @@ public class BatteryController extends BroadcastReceiver { ImageView v = mIconViews.get(i); v.setImageResource(icon); v.setImageLevel(level); + v.setContentDescription(mContext.getString(R.string.accessibility_battery_level, + level)); } N = mLabelViews.size(); for (int i=0; i<N; i++) { //final boolean plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0) != 0; TextView v = mLabelViews.get(i); - v.setText(mContext.getString(R.string.status_bar_settings_battery_meter_format, level)); + v.setText(mContext.getString(R.string.status_bar_settings_battery_meter_format, + level)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java index 0525054..c6f416f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothController.java @@ -19,12 +19,10 @@ package com.android.systemui.statusbar.policy; import java.util.ArrayList; import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothDevice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; -import android.util.Slog; import android.view.View; import android.widget.ImageView; @@ -54,26 +52,24 @@ public class BluetoothController extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) { - int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR); - mEnabled = state == BluetoothAdapter.STATE_ON; - } else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) { - int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, BluetoothAdapter.STATE_DISCONNECTED); - if (state == BluetoothAdapter.STATE_CONNECTED) { - mIconId = R.drawable.stat_sys_data_bluetooth_connected; - } else { - mIconId = R.drawable.stat_sys_data_bluetooth; - } - } + int contentDescriptionResId = 0; + if (state == BluetoothAdapter.STATE_CONNECTED) { + mIconId = R.drawable.stat_sys_data_bluetooth_connected; + contentDescriptionResId = R.string.accessibility_bluetooth_connected; + } else { + mIconId = R.drawable.stat_sys_data_bluetooth; + contentDescriptionResId = R.string.accessibility_bluetooth_disconnected; + } int N = mIconViews.size(); for (int i=0; i<N; i++) { ImageView v = mIconViews.get(i); v.setImageResource(mIconId); v.setVisibility(mEnabled ? View.VISIBLE : View.GONE); + v.setContentDescription(mContext.getString(contentDescriptionResId)); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index 3175a99..829855b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -28,14 +28,11 @@ import android.content.IntentFilter; import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.NetworkInfo; -import android.net.wifi.SupplicantState; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; import android.os.Message; import android.os.Messenger; import android.os.RemoteException; @@ -87,6 +84,11 @@ public class NetworkController extends BroadcastReceiver { int mDataTypeIconId; boolean mDataActive; + String mContentDescriptionPhoneSignal; + String mContentDescriptionWifi; + String mContentDescriptionCombinedSignal; + String mContentDescriptionDataType; + // wifi final WifiManager mWifiManager; AsyncChannel mWifiChannel; @@ -366,6 +368,8 @@ public class NetworkController extends BroadcastReceiver { if (mSignalStrength == null) { mPhoneSignalIconId = R.drawable.stat_sys_signal_null; mDataSignalIconId = R.drawable.stat_sys_signal_0; // note we use 0 instead of null + mContentDescriptionPhoneSignal = mContext.getString( + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0]); } else { int iconLevel; int[] iconList; @@ -385,6 +389,9 @@ public class NetworkController extends BroadcastReceiver { } } mPhoneSignalIconId = iconList[iconLevel]; + mContentDescriptionPhoneSignal = mContext.getString( + AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[iconLevel]); + mDataSignalIconId = TelephonyIcons.DATA_SIGNAL_STRENGTH[mInetCondition][iconLevel]; } } @@ -395,14 +402,20 @@ public class NetworkController extends BroadcastReceiver { case TelephonyManager.NETWORK_TYPE_UNKNOWN: mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; mDataTypeIconId = 0; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_gprs); break; case TelephonyManager.NETWORK_TYPE_EDGE: mDataIconList = TelephonyIcons.DATA_E[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_edge; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_edge); break; case TelephonyManager.NETWORK_TYPE_UMTS: mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_3g; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); break; case TelephonyManager.NETWORK_TYPE_HSDPA: case TelephonyManager.NETWORK_TYPE_HSUPA: @@ -410,19 +423,27 @@ public class NetworkController extends BroadcastReceiver { if (mHspaDataDistinguishable) { mDataIconList = TelephonyIcons.DATA_H[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_hsdpa; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3_5g); } else { mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_3g; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); } break; case TelephonyManager.NETWORK_TYPE_CDMA: // display 1xRTT for IS95A/B mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_1x; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_cdma); break; case TelephonyManager.NETWORK_TYPE_1xRTT: mDataIconList = TelephonyIcons.DATA_1X[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_1x; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_cdma); break; case TelephonyManager.NETWORK_TYPE_EVDO_0: //fall through case TelephonyManager.NETWORK_TYPE_EVDO_A: @@ -430,14 +451,20 @@ public class NetworkController extends BroadcastReceiver { case TelephonyManager.NETWORK_TYPE_EHRPD: mDataIconList = TelephonyIcons.DATA_3G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_3g; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_3g); break; case TelephonyManager.NETWORK_TYPE_LTE: mDataIconList = TelephonyIcons.DATA_4G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_4g; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_4g); break; default: mDataIconList = TelephonyIcons.DATA_G[mInetCondition]; mDataTypeIconId = R.drawable.stat_sys_signal_gprs; + mContentDescriptionDataType = mContext.getString( + R.string.accessibility_data_connection_gprs); break; } if ((isCdma() && isCdmaEri()) || mPhone.isNetworkRoaming()) { @@ -618,8 +645,11 @@ public class NetworkController extends BroadcastReceiver { private void updateWifiIcons() { if (mWifiConnected) { mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[mInetCondition][mWifiLevel]; + mContentDescriptionWifi = mContext.getString( + AccessibilityContentDescriptions.WIFI_CONNECTION_STRENGTH[mWifiLevel]); } else { mWifiIconId = WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]; + mContentDescriptionWifi = mContext.getString(R.string.accessibility_no_wifi); } } @@ -704,6 +734,7 @@ public class NetworkController extends BroadcastReceiver { } } combinedSignalIconId = mWifiIconId; + mContentDescriptionCombinedSignal = mContentDescriptionWifi; dataTypeIconId = 0; } else if (mDataConnected) { label = mNetworkName; @@ -723,22 +754,29 @@ public class NetworkController extends BroadcastReceiver { break; } combinedSignalIconId = mDataSignalIconId; + mContentDescriptionCombinedSignal = mContentDescriptionDataType; dataTypeIconId = mDataTypeIconId; } else if (mBluetoothTethered) { label = mContext.getString(R.string.bluetooth_tethered); combinedSignalIconId = mBluetoothTetherIconId; + mContentDescriptionCombinedSignal = mContext.getString( + R.string.accessibility_bluetooth_tether); dataTypeIconId = 0; } else if (mAirplaneMode && (mServiceState == null || (!hasService() && !mServiceState.isEmergencyOnly()))) { // Only display the flight-mode icon if not in "emergency calls only" mode. label = context.getString(R.string.status_bar_settings_signal_meter_disconnected); combinedSignalIconId = R.drawable.stat_sys_signal_flightmode; + mContentDescriptionCombinedSignal = mContext.getString( + R.string.accessibility_airplane_mode); dataTypeIconId = 0; } else { label = context.getString(R.string.status_bar_settings_signal_meter_disconnected); // On devices without mobile radios, we want to show the wifi icon combinedSignalIconId = hasMobileDataFeature() ? mDataSignalIconId : mWifiIconId; + mContentDescriptionCombinedSignal = hasMobileDataFeature() + ? mContentDescriptionDataType : mContentDescriptionWifi; dataTypeIconId = 0; } @@ -764,6 +802,7 @@ public class NetworkController extends BroadcastReceiver { for (int i=0; i<N; i++) { final ImageView v = mPhoneSignalIconViews.get(i); v.setImageResource(mPhoneSignalIconId); + v.setContentDescription(mContentDescriptionPhoneSignal); } } @@ -774,6 +813,7 @@ public class NetworkController extends BroadcastReceiver { for (int i=0; i<N; i++) { final ImageView v = mDataDirectionIconViews.get(i); v.setImageResource(mDataDirectionIconId); + v.setContentDescription(mContentDescriptionDataType); } } @@ -784,6 +824,7 @@ public class NetworkController extends BroadcastReceiver { for (int i=0; i<N; i++) { final ImageView v = mWifiIconViews.get(i); v.setImageResource(mWifiIconId); + v.setContentDescription(mContentDescriptionWifi); } } @@ -794,6 +835,7 @@ public class NetworkController extends BroadcastReceiver { for (int i=0; i<N; i++) { final ImageView v = mCombinedSignalIconViews.get(i); v.setImageResource(combinedSignalIconId); + v.setContentDescription(mContentDescriptionCombinedSignal); } } @@ -808,6 +850,7 @@ public class NetworkController extends BroadcastReceiver { } else { v.setVisibility(View.VISIBLE); v.setImageResource(dataTypeIconId); + v.setContentDescription(mContentDescriptionDataType); } } } @@ -826,6 +869,7 @@ public class NetworkController extends BroadcastReceiver { } else { v.setVisibility(View.VISIBLE); v.setImageResource(dataDirectionOverlayIconId); + v.setContentDescription(mContentDescriptionDataType); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java index c62c4ad..8c4ae19 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java @@ -22,6 +22,7 @@ import android.content.res.TypedArray; import android.os.RemoteException; import android.util.AttributeSet; import android.util.Slog; +import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; @@ -90,6 +91,19 @@ public class CompatModePanel extends FrameLayout implements StatusBarPanel, return false; } + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Ignore hover events outside of this panel bounds since such events + // generate spurious accessibility events with the panel content when + // tapping outside of it, thus confusing the user. + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { + return super.dispatchHoverEvent(event); + } + return true; + } + public void setTrigger(View v) { mTrigger = v; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java index 339e3f3..1e417ac 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java @@ -30,6 +30,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.Pair; +import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; @@ -160,6 +161,19 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel, } } + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Ignore hover events outside of this panel bounds since such events + // generate spurious accessibility events with the panel content when + // tapping outside of it, thus confusing the user. + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { + return super.dispatchHoverEvent(event); + } + return true; + } + private void updateHardKeyboardEnabled() { if (mHardKeyboardAvailable) { final boolean checked = mHardKeyboardSwitch.isChecked(); @@ -222,6 +236,7 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel, itemSubtitle.setText(imiName); } subtypeIcon.setImageDrawable(icon); + subtypeIcon.setContentDescription(itemTitle.getText()); final String settingsActivity = imi.getSettingsActivity(); if (!TextUtils.isEmpty(settingsActivity)) { settingsIcon.setOnClickListener(new View.OnClickListener() { @@ -463,4 +478,5 @@ public class InputMethodsPanel extends LinearLayout implements StatusBarPanel, public interface OnHardKeyboardEnabledChangeListener { public void onHardKeyboardEnabledChange(boolean enabled); } + } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationArea.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationArea.java new file mode 100644 index 0000000..42bdf3d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationArea.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tablet; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.LinearLayout; + +public class NotificationArea extends LinearLayout { + + public NotificationArea(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (super.onRequestSendAccessibilityEvent(child, event)) { + // The event is coming from a descendant like battery but append + // the content of the entire notification area so accessibility + // services can choose how to present the content to the user. + AccessibilityEvent record = AccessibilityEvent.obtain(); + onInitializeAccessibilityEvent(record); + dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java index 64a4f16..a316e4b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java @@ -20,27 +20,15 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.content.Context; -import android.content.res.Resources; -import android.graphics.Canvas; import android.graphics.Rect; -import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Slog; -import android.view.accessibility.AccessibilityEvent; import android.view.LayoutInflater; import android.view.MotionEvent; -import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.widget.FrameLayout; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.RelativeLayout; -import android.widget.TextView; import com.android.systemui.R; @@ -53,8 +41,7 @@ public class NotificationPanel extends RelativeLayout implements StatusBarPanel, boolean mShowing; int mNotificationCount = 0; - View mTitleArea; - ModeToggle mModeToggle; + NotificationPanelTitle mTitleArea; View mSettingsButton; View mNotificationButton; View mNotificationScroller; @@ -68,48 +55,6 @@ public class NotificationPanel extends RelativeLayout implements StatusBarPanel, Choreographer mChoreo = new Choreographer(); - static class ModeToggle extends View { - NotificationPanel mPanel; - View mTitle; - public ModeToggle(Context context, AttributeSet attrs) { - super(context, attrs); - } - public void setPanel(NotificationPanel p) { - mPanel = p; - } - public void setTitleArea(View v) { - mTitle = v; - } - @Override - public boolean onTouchEvent(MotionEvent e) { - final int x = (int)e.getX(); - final int y = (int)e.getY(); - switch (e.getAction()) { - case MotionEvent.ACTION_DOWN: - mTitle.setPressed(true); - break; - case MotionEvent.ACTION_MOVE: - mTitle.setPressed(x >= 0 - && x < getWidth() - && y >= 0 - && y < getHeight()); - break; - case MotionEvent.ACTION_CANCEL: - mTitle.setPressed(false); - break; - case MotionEvent.ACTION_UP: - if (mTitle.isPressed()) { - sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); - playSoundEffect(SoundEffectConstants.CLICK); - mPanel.swapPanels(); - mTitle.setPressed(false); - } - break; - } - return true; - } - } - public NotificationPanel(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -126,14 +71,11 @@ public class NotificationPanel extends RelativeLayout implements StatusBarPanel, mContentParent = (ViewGroup)findViewById(R.id.content_parent); mContentParent.bringToFront(); - mTitleArea = findViewById(R.id.title_area); - mModeToggle = (ModeToggle) findViewById(R.id.mode_toggle); - mModeToggle.setOnClickListener(this); - mModeToggle.setPanel(this); - mModeToggle.setTitleArea(mTitleArea); + mTitleArea = (NotificationPanelTitle) findViewById(R.id.title_area); + mTitleArea.setPanel(this); - mSettingsButton = (ImageView)findViewById(R.id.settings_button); - mNotificationButton = (ImageView)findViewById(R.id.notification_button); + mSettingsButton = findViewById(R.id.settings_button); + mNotificationButton = findViewById(R.id.notification_button); mNotificationScroller = findViewById(R.id.notification_scroller); mContentFrame = (ViewGroup)findViewById(R.id.content_frame); @@ -185,6 +127,19 @@ public class NotificationPanel extends RelativeLayout implements StatusBarPanel, } } + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Ignore hover events outside of this panel bounds since such events + // generate spurious accessibility events with the panel content when + // tapping outside of it, thus confusing the user. + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { + return super.dispatchHoverEvent(event); + } + return true; + } + /* @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { @@ -205,7 +160,7 @@ public class NotificationPanel extends RelativeLayout implements StatusBarPanel, */ public void onClick(View v) { - if (v == mModeToggle) { + if (v == mTitleArea) { swapPanels(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanelTitle.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanelTitle.java new file mode 100644 index 0000000..689bc36 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanelTitle.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.tablet; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.SoundEffectConstants; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.widget.RelativeLayout; + +public class NotificationPanelTitle extends RelativeLayout implements View.OnClickListener { + private NotificationPanel mPanel; + + public NotificationPanelTitle(Context context, AttributeSet attrs) { + super(context, attrs); + setOnClickListener(this); + } + + public void setPanel(NotificationPanel p) { + mPanel = p; + } + + @Override + public boolean onTouchEvent(MotionEvent e) { + switch (e.getAction()) { + case MotionEvent.ACTION_DOWN: + setPressed(true); + break; + case MotionEvent.ACTION_MOVE: + final int x = (int) e.getX(); + final int y = (int) e.getY(); + setPressed(x > 0 && x < getWidth() && y > 0 && y < getHeight()); + break; + case MotionEvent.ACTION_UP: + if (isPressed()) { + playSoundEffect(SoundEffectConstants.CLICK); + mPanel.swapPanels(); + setPressed(false); + } + break; + case MotionEvent.ACTION_CANCEL: + setPressed(false); + break; + } + return true; + } + + @Override + public void onClick(View v) { + if (v == this) { + mPanel.swapPanels(); + } + } + + @Override + public boolean onRequestSendAccessibilityEvent(View child, AccessibilityEvent event) { + if (super.onRequestSendAccessibilityEvent(child, event)) { + AccessibilityEvent record = AccessibilityEvent.obtain(); + onInitializeAccessibilityEvent(record); + dispatchPopulateAccessibilityEvent(record); + event.appendRecord(record); + return true; + } + return false; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java index 8b68240..ba28306 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPeekPanel.java @@ -18,12 +18,9 @@ package com.android.systemui.statusbar.tablet; import android.content.Context; import android.util.AttributeSet; -import android.util.Slog; import android.view.MotionEvent; import android.widget.RelativeLayout; -import com.android.systemui.R; - public class NotificationPeekPanel extends RelativeLayout implements StatusBarPanel { TabletStatusBar mBar; @@ -54,5 +51,17 @@ public class NotificationPeekPanel extends RelativeLayout implements StatusBarPa mBar.resetNotificationPeekFadeTimer(); return false; } -} + @Override + public boolean dispatchHoverEvent(MotionEvent event) { + // Ignore hover events outside of this panel bounds since such events + // generate spurious accessibility events with the panel content when + // tapping outside of it, thus confusing the user. + final int x = (int) event.getX(); + final int y = (int) event.getY(); + if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { + return super.dispatchHoverEvent(event); + } + return true; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index df09f84..13846ed 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -808,7 +808,8 @@ public class TabletStatusBar extends StatusBar implements // Update the icon. final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, notification.notification.iconLevel, - notification.notification.number); + notification.notification.number, + notification.notification.tickerText); if (!oldEntry.icon.set(ic)) { handleNotificationError(key, notification, "Couldn't update icon: " + ic); return; @@ -1012,10 +1013,7 @@ public class TabletStatusBar extends StatusBar implements mCompatModeButton.refresh(); if (mCompatModeButton.getVisibility() == View.VISIBLE) { - if (DEBUG_COMPAT_HELP - || ! Prefs.read(mContext).getBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, false)) { showCompatibilityHelp(); - } } else { hideCompatibilityHelp(); mCompatModePanel.closePanel(); @@ -1451,13 +1449,15 @@ public class TabletStatusBar extends StatusBar implements } // Construct the icon. final StatusBarIconView iconView = new StatusBarIconView(mContext, - notification.pkg + "/0x" + Integer.toHexString(notification.id)); + notification.pkg + "/0x" + Integer.toHexString(notification.id), + notification.notification); iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); final StatusBarIcon ic = new StatusBarIcon(notification.pkg, notification.notification.icon, notification.notification.iconLevel, - notification.notification.number); + notification.notification.number, + notification.notification.tickerText); if (!iconView.set(ic)) { handleNotificationError(key, notification, "Couldn't attach StatusBarIcon: " + ic); return null; @@ -1501,11 +1501,6 @@ public class TabletStatusBar extends StatusBar implements // alternate behavior in DND mode if (mNotificationDNDMode) { if (mIconLayout.getChildCount() == 0) { - final StatusBarIconView iconView = new StatusBarIconView(mContext, "_dnd"); - iconView.setImageResource(R.drawable.ic_notification_dnd); - iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); - iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0); - final Notification dndNotification = new Notification.Builder(mContext) .setContentTitle(mContext.getText(R.string.notifications_off_title)) .setContentText(mContext.getText(R.string.notifications_off_text)) @@ -1513,6 +1508,12 @@ public class TabletStatusBar extends StatusBar implements .setOngoing(true) .getNotification(); + final StatusBarIconView iconView = new StatusBarIconView(mContext, "_dnd", + dndNotification); + iconView.setImageResource(R.drawable.ic_notification_dnd); + iconView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + iconView.setPadding(mIconHPadding, 0, mIconHPadding, 0); + mNotificationDNDDummyEntry = new NotificationData.Entry( null, new StatusBarNotification("", 0, "", 0, 0, dndNotification), @@ -1634,19 +1635,24 @@ public class TabletStatusBar extends StatusBar implements } else { if ((sbn.notification.flags & Notification.FLAG_ONGOING_EVENT) == 0) { vetoButton.setVisibility(View.INVISIBLE); + vetoButton.setContentDescription("VETO"); } else { vetoButton.setVisibility(View.GONE); } } + vetoButton.setContentDescription(mContext.getString( + R.string.accessibility_remove_notification)); // the large icon ImageView largeIcon = (ImageView)row.findViewById(R.id.large_icon); if (sbn.notification.largeIcon != null) { largeIcon.setImageBitmap(sbn.notification.largeIcon); + largeIcon.setContentDescription(sbn.notification.tickerText); } else { largeIcon.getLayoutParams().width = 0; largeIcon.setVisibility(View.INVISIBLE); } + largeIcon.setContentDescription(sbn.notification.tickerText); // bind the click event to the content area ViewGroup content = (ViewGroup)row.findViewById(R.id.content); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java index a8f4262..6045e31 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java @@ -284,7 +284,7 @@ public class TabletTicker } else if (n.tickerText != null) { group = (ViewGroup)inflater.inflate(R.layout.status_bar_ticker_compat, mWindow, false); final Drawable icon = StatusBarIconView.getIcon(mContext, - new StatusBarIcon(notification.pkg, n.icon, n.iconLevel, 0)); + new StatusBarIcon(notification.pkg, n.icon, n.iconLevel, 0, n.tickerText)); ImageView iv = (ImageView)group.findViewById(iconId); iv.setImageDrawable(icon); iv.setVisibility(View.VISIBLE); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java index 935f4ad..47d34b3 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardViewMediator.java @@ -35,7 +35,6 @@ import android.media.Ringtone; import android.media.RingtoneManager; import android.net.Uri; import android.os.Handler; -import android.os.IBinder; import android.os.LocalPowerManager; import android.os.Message; import android.os.PowerManager; @@ -43,7 +42,6 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; -import android.provider.Settings.SettingNotFoundException; import android.telephony.TelephonyManager; import android.util.EventLog; import android.util.Log; @@ -1117,8 +1115,11 @@ public class KeyguardViewMediator implements KeyguardViewCallback, // Give feedback to user when secure keyguard is active and engaged if (mShowing && isSecure()) { if (!mShowingLockIcon) { + String contentDescription = mContext.getString( + com.android.internal.R.string.status_bar_device_locked); mStatusBarManager.setIcon("secure", - com.android.internal.R.drawable.stat_sys_secure, 0); + com.android.internal.R.drawable.stat_sys_secure, 0, + contentDescription); mShowingLockIcon = true; } } else { diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 2597978..7399679 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -50,6 +50,7 @@ import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.content.res.Configuration; @@ -1046,7 +1047,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mStatusBar.setIconVisibility("ime", false); } else if (packageName != null) { if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); - mStatusBar.setIcon("ime", packageName, iconId, 0); + CharSequence contentDescription = null; + try { + PackageManager packageManager = mContext.getPackageManager(); + contentDescription = packageManager.getApplicationLabel( + packageManager.getApplicationInfo(packageName, 0)); + } catch (NameNotFoundException nnfe) { + /* ignore */ + } + mStatusBar.setIcon("ime", packageName, iconId, 0, + contentDescription != null ? contentDescription.toString() : null); mStatusBar.setIconVisibility("ime", true); } } diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 286a937..4ced83c 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -22,10 +22,10 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.os.IBinder; -import android.os.RemoteException; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; import android.util.Slog; import android.view.View; @@ -175,7 +175,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub } } - public void setIcon(String slot, String iconPackage, int iconId, int iconLevel) { + public void setIcon(String slot, String iconPackage, int iconId, int iconLevel, + String contentDescription) { enforceStatusBar(); synchronized (mIcons) { @@ -184,7 +185,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub throw new SecurityException("invalid status bar icon slot: " + slot); } - StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel); + StatusBarIcon icon = new StatusBarIcon(iconPackage, iconId, iconLevel, 0, + contentDescription); //Slog.d(TAG, "setIcon slot=" + slot + " index=" + index + " icon=" + icon); mIcons.setIcon(index, icon); diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java index b212533..e75a079 100644 --- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java +++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java @@ -100,22 +100,22 @@ public class StatusBarTest extends TestActivity new Test("Double Remove") { public void run() { Log.d(TAG, "set 0"); - mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0); + mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0, null); Log.d(TAG, "remove 1"); mStatusBarManager.removeIcon("tty"); SystemClock.sleep(1000); Log.d(TAG, "set 1"); - mStatusBarManager.setIcon("tty", R.drawable.stat_sys_phone, 0); + mStatusBarManager.setIcon("tty", R.drawable.stat_sys_phone, 0, null); if (false) { Log.d(TAG, "set 2"); - mStatusBarManager.setIcon("tty", R.drawable.stat_sys_phone, 0); + mStatusBarManager.setIcon("tty", R.drawable.stat_sys_phone, 0, null); } Log.d(TAG, "remove 2"); mStatusBarManager.removeIcon("tty"); Log.d(TAG, "set 3"); - mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0); + mStatusBarManager.setIcon("speakerphone", R.drawable.stat_sys_phone, 0, null); } }, new Test("Hide (FLAG_FULLSCREEN)") { |