From 20aef8a2914478a85aa679ec31bf739c7818eb3c Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Wed, 4 May 2016 16:44:08 -0400 Subject: [PATCH] Quicksettings accessibility. Bug: 15696340 Change-Id: I6887e2dad4822911d3a1642aaec5703174b57330 --- .../SystemUI/res/layout/qs_paged_tile_layout.xml | 3 +- .../layout/quick_status_bar_expanded_header.xml | 15 ++++-- .../com/android/systemui/qs/PagedTileLayout.java | 1 + .../src/com/android/systemui/qs/QSPanel.java | 14 ++++-- .../src/com/android/systemui/qs/QSTile.java | 23 ++++++--- .../com/android/systemui/qs/QSTileBaseView.java | 55 +++++++++++++++++++++- .../src/com/android/systemui/qs/QSTileView.java | 6 ++- .../src/com/android/systemui/qs/QuickQSPanel.java | 26 ++++++++-- .../src/com/android/systemui/qs/TileLayout.java | 1 + .../systemui/qs/tiles/AirplaneModeTile.java | 8 ++-- .../com/android/systemui/qs/tiles/BatteryTile.java | 9 +++- .../android/systemui/qs/tiles/BluetoothTile.java | 21 +++++++-- .../com/android/systemui/qs/tiles/CastTile.java | 8 ++++ .../android/systemui/qs/tiles/CellularTile.java | 31 +++++++++--- .../systemui/qs/tiles/ColorInversionTile.java | 4 ++ .../android/systemui/qs/tiles/DataSaverTile.java | 6 ++- .../src/com/android/systemui/qs/tiles/DndTile.java | 5 +- .../android/systemui/qs/tiles/FlashlightTile.java | 9 ++-- .../com/android/systemui/qs/tiles/HotspotTile.java | 5 ++ .../android/systemui/qs/tiles/LocationTile.java | 4 ++ .../systemui/qs/tiles/RotationLockTile.java | 35 +++++++------- .../com/android/systemui/qs/tiles/WifiTile.java | 55 ++++++++++++++-------- .../android/systemui/qs/tiles/WorkModeTile.java | 4 ++ .../statusbar/phone/ExpandableIndicator.java | 7 +++ .../systemui/statusbar/phone/MultiUserSwitch.java | 38 ++++++++------- .../statusbar/phone/QuickStatusBarHeader.java | 8 +++- .../statusbar/policy/WifiSignalController.java | 6 +++ 27 files changed, 307 insertions(+), 100 deletions(-) diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml index 68129cee28cc..ee55ec2d3ce2 100644 --- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml +++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml @@ -47,7 +47,8 @@ android:textAppearance="@style/TextAppearance.QS.DetailButton" android:textColor="#64FFFFFF" android:focusable="true" - android:text="@string/qs_edit" /> + android:text="@string/qs_edit" + android:contentDescription="@string/accessibility_quick_settings_edit"/> diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index a4f9df35ca55..86119426a3e7 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -26,6 +26,7 @@ android:clipChildren="false" android:clipToPadding="false" android:baselineAligned="false" + android:clickable="false" > + android:contentDescription="@string/accessibility_quick_settings_settings" /> + android:gravity="center_vertical" + android:focusable="true" /> + android:orientation="horizontal" + android:focusable="true" > + android:clipToPadding="false" + android:importantForAccessibility="yes" + android:focusable="true" + android:accessibilityTraversalAfter="@id/date_time_group" + android:accessibilityTraversalBefore="@id/expand_indicator" /> > tiles) { + setTiles(tiles, false); + } + + public void setTiles(Collection> tiles, boolean collapsedView) { for (TileRecord record : mRecords) { mTileLayout.removeTile(record); } mRecords.clear(); for (QSTile tile : tiles) { - addTile(tile); + addTile(tile, collapsedView); } } @@ -285,14 +289,14 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { r.tileView.onStateChanged(state); } - protected QSTileBaseView createTileView(QSTile tile) { - return new QSTileView(mContext, tile.createTileView(mContext)); + protected QSTileBaseView createTileView(QSTile tile, boolean collapsedView) { + return new QSTileView(mContext, tile.createTileView(mContext), collapsedView); } - protected void addTile(final QSTile tile) { + protected void addTile(final QSTile tile, boolean collapsedView) { final TileRecord r = new TileRecord(); r.tile = tile; - r.tileView = createTileView(tile); + r.tileView = createTileView(tile, collapsedView); final QSTile.Callback callback = new QSTile.Callback() { @Override public void onStateChanged(QSTile.State state) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java index 76925981c8f5..0cc30a8642ea 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java @@ -513,9 +513,12 @@ public abstract class QSTile implements Listenable { public CharSequence label; public CharSequence contentDescription; public CharSequence dualLabelContentDescription; + public CharSequence minimalContentDescription; public boolean autoMirrorDrawable = true; public boolean disabledByPolicy; public EnforcedAdmin enforcedAdmin; + public String minimalAccessibilityClassName; + public String expandedAccessibilityClassName; public boolean copyTo(State other) { if (other == null) throw new IllegalArgumentException(); @@ -526,12 +529,21 @@ public abstract class QSTile implements Listenable { || !Objects.equals(other.autoMirrorDrawable, autoMirrorDrawable) || !Objects.equals(other.dualLabelContentDescription, dualLabelContentDescription) + || !Objects.equals(other.minimalContentDescription, + minimalContentDescription) + || !Objects.equals(other.minimalAccessibilityClassName, + minimalAccessibilityClassName) + || !Objects.equals(other.expandedAccessibilityClassName, + expandedAccessibilityClassName) || !Objects.equals(other.disabledByPolicy, disabledByPolicy) || !Objects.equals(other.enforcedAdmin, enforcedAdmin); other.icon = icon; other.label = label; other.contentDescription = contentDescription; other.dualLabelContentDescription = dualLabelContentDescription; + other.minimalContentDescription = minimalContentDescription; + other.minimalAccessibilityClassName = minimalAccessibilityClassName; + other.expandedAccessibilityClassName = expandedAccessibilityClassName; other.autoMirrorDrawable = autoMirrorDrawable; other.disabledByPolicy = disabledByPolicy; if (enforcedAdmin == null) { @@ -555,6 +567,9 @@ public abstract class QSTile implements Listenable { sb.append(",label=").append(label); sb.append(",contentDescription=").append(contentDescription); sb.append(",dualLabelContentDescription=").append(dualLabelContentDescription); + sb.append(",minimalContentDescription=").append(minimalContentDescription); + sb.append(",minimalAccessibilityClassName=").append(minimalAccessibilityClassName); + sb.append(",expandedAccessibilityClassName=").append(expandedAccessibilityClassName); sb.append(",autoMirrorDrawable=").append(autoMirrorDrawable); sb.append(",disabledByPolicy=").append(disabledByPolicy); sb.append(",enforcedAdmin=").append(enforcedAdmin); @@ -581,8 +596,7 @@ public abstract class QSTile implements Listenable { } } - public static final class SignalState extends State { - public boolean enabled; + public static final class SignalState extends BooleanState { public boolean connected; public boolean activityIn; public boolean activityOut; @@ -593,12 +607,10 @@ public abstract class QSTile implements Listenable { @Override public boolean copyTo(State other) { final SignalState o = (SignalState) other; - final boolean changed = o.enabled != enabled - || o.connected != connected || o.activityIn != activityIn + final boolean changed = o.connected != connected || o.activityIn != activityIn || o.activityOut != activityOut || o.overlayIconId != overlayIconId || o.isOverlayIconWide != isOverlayIconWide; - o.enabled = enabled; o.connected = connected; o.activityIn = activityIn; o.activityOut = activityOut; @@ -611,7 +623,6 @@ public abstract class QSTile implements Listenable { @Override protected StringBuilder toStringBuilder() { final StringBuilder rt = super.toStringBuilder(); - rt.insert(rt.length() - 1, ",enabled=" + enabled); rt.insert(rt.length() - 1, ",connected=" + connected); rt.insert(rt.length() - 1, ",activityIn=" + activityIn); rt.insert(rt.length() - 1, ",activityOut=" + activityOut); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java index 44b38f1b6e6a..f05aa3cdd89c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java @@ -22,8 +22,12 @@ import android.graphics.drawable.RippleDrawable; import android.os.Handler; import android.os.Looper; import android.os.Message; +import android.text.TextUtils; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import android.widget.LinearLayout; +import android.widget.Switch; import com.android.systemui.R; @@ -33,8 +37,15 @@ public class QSTileBaseView extends LinearLayout { private QSIconView mIcon; private RippleDrawable mRipple; private Drawable mTileBackground; + private String mAccessibilityClass; + private boolean mTileState; + private boolean mCollapsedView; public QSTileBaseView(Context context, QSIconView icon) { + this(context, icon, false); + } + + public QSTileBaseView(Context context, QSIconView icon, boolean collapsedView) { super(context); mIcon = icon; addView(mIcon); @@ -51,6 +62,7 @@ public class QSTileBaseView extends LinearLayout { setPadding(0, padding, 0, padding); setClipChildren(false); setClipToPadding(false); + mCollapsedView = collapsedView; } private Drawable newTileBackground() { @@ -116,13 +128,54 @@ public class QSTileBaseView extends LinearLayout { protected void handleStateChanged(QSTile.State state) { mIcon.setIcon(state); - setContentDescription(state.contentDescription); + if (mCollapsedView && !TextUtils.isEmpty(state.minimalContentDescription)) { + setContentDescription(state.minimalContentDescription); + } else { + setContentDescription(state.contentDescription); + } + if (mCollapsedView) { + mAccessibilityClass = state.minimalAccessibilityClassName; + } else { + mAccessibilityClass = state.expandedAccessibilityClassName; + } + if (state instanceof QSTile.BooleanState) { + mTileState = ((QSTile.BooleanState) state).value; + } } public QSIconView getIcon() { return mIcon; } + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + if (!TextUtils.isEmpty(mAccessibilityClass)) { + event.setClassName(mAccessibilityClass); + if (Switch.class.getName().equals(mAccessibilityClass)) { + String label = getResources() + .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off); + event.setContentDescription(label); + event.setChecked(mTileState); + } + } + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + if (!TextUtils.isEmpty(mAccessibilityClass)) { + info.setClassName(mAccessibilityClass); + if (Switch.class.getName().equals(mAccessibilityClass)) { + String label = getResources() + .getString(mTileState ? R.string.switch_bar_on : R.string.switch_bar_off); + info.setText(label); + info.setChecked(mTileState); + info.setCheckable(true); + } + } + } + private class H extends Handler { private static final int STATE_CHANGED = 1; public H() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java index c3766e89402e..924779494bc1 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileView.java @@ -40,7 +40,11 @@ public class QSTileView extends QSTileBaseView { private ImageView mPadLock; public QSTileView(Context context, QSIconView icon) { - super(context, icon); + this(context, icon, false); + } + + public QSTileView(Context context, QSIconView icon, boolean collapsedView) { + super(context, icon, collapsedView); mContext = context; final Resources res = context.getResources(); diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 2ef3672c69f0..a11a6e59009f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -23,6 +23,7 @@ import android.view.Gravity; import android.view.View; import android.widget.LinearLayout; import android.widget.Space; + import com.android.systemui.R; import com.android.systemui.qs.QSTile.SignalState; import com.android.systemui.qs.QSTile.State; @@ -93,8 +94,8 @@ public class QuickQSPanel extends QSPanel { } @Override - protected QSTileBaseView createTileView(QSTile tile) { - return new QSTileBaseView(mContext, tile.createTileView(mContext)); + protected QSTileBaseView createTileView(QSTile tile, boolean collapsedView) { + return new QSTileBaseView(mContext, tile.createTileView(mContext), collapsedView); } @Override @@ -133,7 +134,7 @@ public class QuickQSPanel extends QSPanel { break; } } - super.setTiles(quickTiles); + super.setTiles(quickTiles, true); } private final Tunable mNumTiles = new Tunable() { @@ -150,6 +151,7 @@ public class QuickQSPanel extends QSPanel { private static class HeaderTileLayout extends LinearLayout implements QSTileLayout { private final Space mEndSpacer; + protected final ArrayList mRecords = new ArrayList<>(); public HeaderTileLayout(Context context) { super(context); @@ -185,6 +187,7 @@ public class QuickQSPanel extends QSPanel { // Add a spacer. addView(new Space(mContext), getChildCount() - 1 /* Leave icon at end */, generateSpaceParams()); + mRecords.add(tile); } private LayoutParams generateSpaceParams() { @@ -209,6 +212,7 @@ public class QuickQSPanel extends QSPanel { removeViewAt(childIndex); // Remove its spacer as well. removeViewAt(childIndex); + mRecords.remove(tile); } private int getChildIndex(QSTileBaseView tileView) { @@ -236,5 +240,21 @@ public class QuickQSPanel extends QSPanel { public boolean hasOverlappingRendering() { return false; } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mRecords != null && mRecords.size() > 0) { + View previousView = this; + for (TileRecord record : mRecords) { + if (record.tileView.getVisibility() == GONE) continue; + previousView = record.tileView.updateAccessibilityOrder(previousView); + } + mRecords.get(0).tileView.setAccessibilityTraversalAfter( + R.id.alarm_status_collapsed); + mRecords.get(mRecords.size() - 1).tileView.setAccessibilityTraversalBefore( + R.id.expand_indicator); + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 1e3c458d00bd..a578e6c9cb9e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -32,6 +32,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { public TileLayout(Context context, AttributeSet attrs) { super(context, attrs); + setFocusableInTouchMode(true); updateResources(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java index 5f5a87e3cb87..a980a7f87405 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java @@ -23,6 +23,7 @@ import android.content.IntentFilter; import android.net.ConnectivityManager; import android.provider.Settings; import android.provider.Settings.Global; +import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -88,13 +89,12 @@ public class AirplaneModeTile extends QSTile { state.label = mContext.getString(R.string.airplane_mode); if (airplaneMode) { state.icon = mEnable; - state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_airplane_on); } else { state.icon = mDisable; - state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_airplane_off); } + state.contentDescription = state.label; + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java index 77eaa3b5acd3..985bc9fdf4bb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatteryTile.java @@ -31,6 +31,7 @@ import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.widget.Button; import android.widget.Checkable; import android.widget.ImageView; import android.widget.TextView; @@ -131,7 +132,13 @@ public class BatteryTile extends QSTile implements BatteryControll }; state.label = percentage; state.contentDescription = mContext.getString(R.string.accessibility_quick_settings_battery, - percentage); + percentage) + "," + + (mPowerSave ? mContext.getString(R.string.battery_saver_notification_title) + : mCharging ? mContext.getString(R.string.expanded_header_battery_charging) + : "") + + "," + mContext.getString(R.string.accessibility_battery_details); + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Button.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java index 63c85dbd6c37..7a2391043fef 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java @@ -24,6 +24,8 @@ import android.provider.Settings; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -107,22 +109,31 @@ public class BluetoothTile extends QSTile { final boolean connecting = mController.isBluetoothConnecting(); state.value = enabled; state.autoMirrorDrawable = false; + state.minimalContentDescription = + mContext.getString(R.string.accessibility_quick_settings_bluetooth); if (enabled) { state.label = null; if (connected) { state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connected); - state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_bluetooth_connected); state.label = mController.getLastDeviceName(); + state.contentDescription = mContext.getString( + R.string.accessibility_bluetooth_name, state.label); + state.minimalContentDescription = state.minimalContentDescription + "," + + state.contentDescription; } else if (connecting) { state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_connecting); state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_bluetooth_connecting); state.label = mContext.getString(R.string.quick_settings_bluetooth_label); + state.minimalContentDescription = state.minimalContentDescription + "," + + state.contentDescription; } else { state.icon = ResourceIcon.get(R.drawable.ic_qs_bluetooth_on); state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_bluetooth_on); + R.string.accessibility_quick_settings_bluetooth_on) + "," + + mContext.getString(R.string.accessibility_not_connected); + state.minimalContentDescription = state.minimalContentDescription + "," + + mContext.getString(R.string.accessibility_not_connected); } if (TextUtils.isEmpty(state.label)) { state.label = mContext.getString(R.string.quick_settings_bluetooth_label); @@ -140,6 +151,10 @@ public class BluetoothTile extends QSTile { R.string.accessibility_bluetooth_name, state.label); } state.dualLabelContentDescription = bluetoothName; + state.contentDescription = state.contentDescription + "," + mContext.getString( + R.string.accessibility_quick_settings_open_settings, getTileLabel()); + state.expandedAccessibilityClassName = Button.class.getName(); + state.minimalAccessibilityClassName = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index bea1e15ffe2b..c3e9b6e4dcc9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -23,6 +23,7 @@ import android.util.Log; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; +import android.widget.Button; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -115,6 +116,7 @@ public class CastTile extends QSTile { @Override protected void handleUpdateState(BooleanState state, Object arg) { state.label = mContext.getString(R.string.quick_settings_cast_title); + state.contentDescription = state.label; state.value = false; state.autoMirrorDrawable = false; final Set devices = mController.getCastDevices(); @@ -123,6 +125,8 @@ public class CastTile extends QSTile { if (device.state == CastDevice.STATE_CONNECTED) { state.value = true; state.label = getDeviceName(device); + state.contentDescription = state.contentDescription + "," + + mContext.getString(R.string.accessibility_cast_name, state.label); } else if (device.state == CastDevice.STATE_CONNECTING) { connecting = true; } @@ -133,6 +137,10 @@ public class CastTile extends QSTile { state.icon = ResourceIcon.get(state.value ? R.drawable.ic_qs_cast_on : R.drawable.ic_qs_cast_off); mDetailAdapter.updateItems(devices); + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName = + Button.class.getName(); + state.contentDescription = state.contentDescription + "," + + mContext.getString(R.string.accessibility_quick_settings_open_details); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java index 55b00b5c4030..18191cf782dc 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java @@ -23,6 +23,8 @@ import android.content.res.Resources; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -132,13 +134,28 @@ public class CellularTile extends QSTile { final String signalContentDesc = cb.enabled && (cb.mobileSignalIconId > 0) ? cb.signalContentDescription : r.getString(R.string.accessibility_no_signal); - final String dataContentDesc = cb.enabled && (cb.dataTypeIconId > 0) && !cb.wifiEnabled - ? cb.dataContentDescription - : r.getString(R.string.accessibility_no_data); - state.contentDescription = r.getString( - R.string.accessibility_quick_settings_mobile, - signalContentDesc, dataContentDesc, - state.label); + + if (cb.noSim) { + state.contentDescription = state.label; + } else { + String enabledDesc = cb.enabled ? r.getString(R.string.accessibility_cell_data_on) + : r.getString(R.string.accessibility_cell_data_off); + + state.contentDescription = r.getString( + R.string.accessibility_quick_settings_mobile, + enabledDesc, signalContentDesc, + state.label); + state.minimalContentDescription = r.getString( + R.string.accessibility_quick_settings_mobile, + r.getString(R.string.accessibility_cell_data), signalContentDesc, + state.label); + } + state.contentDescription = state.contentDescription + "," + r.getString( + R.string.accessibility_quick_settings_open_settings, getTileLabel()); + state.expandedAccessibilityClassName = Button.class.getName(); + state.minimalAccessibilityClassName = Switch.class.getName(); + state.value = mDataController.isMobileDataSupported() + && mDataController.isMobileDataEnabled(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java index 416132e6d2a9..5ae7a767aaea 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java @@ -19,6 +19,7 @@ package com.android.systemui.qs.tiles; import android.content.Intent; import android.provider.Settings; import android.provider.Settings.Secure; +import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -96,6 +97,9 @@ public class ColorInversionTile extends QSTile { state.value = enabled; state.label = mContext.getString(R.string.quick_settings_inversion_label); state.icon = enabled ? mEnable : mDisable; + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); + state.contentDescription = state.label; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java index b22e1ec5d533..aabafe162f0c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java @@ -16,6 +16,7 @@ package com.android.systemui.qs.tiles; import android.content.DialogInterface; import android.content.Intent; +import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -96,10 +97,11 @@ public class DataSaverTile extends QSTile implements state.value = arg instanceof Boolean ? (Boolean) arg : mDataSaverController.isDataSaverEnabled(); state.label = mContext.getString(R.string.data_saver); - state.contentDescription = mContext.getString(state.value - ? R.string.accessibility_data_saver_on : R.string.accessibility_data_saver_off); + state.contentDescription = state.label; state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver : R.drawable.ic_data_saver_off); + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java index 11efd56b4b5f..04cb55331554 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java @@ -29,6 +29,7 @@ import android.view.LayoutInflater; import android.view.View; import android.view.View.OnAttachStateChangeListener; import android.view.ViewGroup; +import android.widget.Switch; import android.widget.Toast; import com.android.internal.logging.MetricsLogger; @@ -162,7 +163,7 @@ public class DndTile extends QSTile { state.icon = TOTAL_SILENCE.equals(state.icon) ? mDisableTotalSilence : mDisable; state.label = mContext.getString(R.string.quick_settings_dnd_label); state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_dnd_off); + R.string.accessibility_quick_settings_dnd); break; } if (mShowingDetail && !state.value) { @@ -171,6 +172,8 @@ public class DndTile extends QSTile { if (valueChanged) { fireToggleStateChanged(state.value); } + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java index 69e71bc30436..5ff0bd0156ae 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java @@ -22,6 +22,8 @@ import android.graphics.drawable.Drawable; import android.provider.MediaStore; import android.text.SpannableStringBuilder; import android.text.style.ForegroundColorSpan; +import android.widget.Switch; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -122,10 +124,9 @@ public class FlashlightTile extends QSTile implements } final AnimationIcon icon = state.value ? mEnable : mDisable; state.icon = icon; - int onOrOffId = state.value - ? R.string.accessibility_quick_settings_flashlight_on - : R.string.accessibility_quick_settings_flashlight_off; - state.contentDescription = mContext.getString(onOrOffId); + state.contentDescription = mContext.getString(R.string.quick_settings_flashlight_label); + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java index 25a3eff2b2b2..3587262400d5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java @@ -20,6 +20,8 @@ import android.content.Intent; import android.os.UserManager; import android.provider.Settings; +import android.widget.Switch; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -94,6 +96,9 @@ public class HotspotTile extends QSTile { state.value = mController.isHotspotEnabled(); } state.icon = state.value ? mEnable : mDisable; + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); + state.contentDescription = state.label; } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java index 6286f5e08a8e..5b5ecae3d423 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java @@ -20,6 +20,8 @@ import android.content.Intent; import android.os.UserManager; import android.provider.Settings; +import android.widget.Switch; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -113,6 +115,8 @@ public class LocationTile extends QSTile { state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_location_off); } + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java index 38b3706ee190..521df37b732c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java @@ -21,6 +21,8 @@ import android.content.Intent; import android.content.res.Configuration; import android.provider.Settings; +import android.widget.Switch; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -106,10 +108,9 @@ public class RotationLockTile extends QSTile { state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label); state.icon = portrait ? mPortraitToAuto : mLandscapeToAuto; } - state.contentDescription = getAccessibilityString(rotationLocked, - R.string.accessibility_rotation_lock_on_portrait, - R.string.accessibility_rotation_lock_on_landscape, - R.string.accessibility_rotation_lock_off); + state.contentDescription = getAccessibilityString(rotationLocked); + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } public static boolean isCurrentOrientationLockPortrait(RotationLockController controller, @@ -133,29 +134,25 @@ public class RotationLockTile extends QSTile { * Get the correct accessibility string based on the state * * @param locked Whether or not rotation is locked. - * @param idWhenPortrait The id which should be used when locked in portrait. - * @param idWhenLandscape The id which should be used when locked in landscape. - * @param idWhenOff The id which should be used when the rotation lock is off. - * @return */ - private String getAccessibilityString(boolean locked, int idWhenPortrait, int idWhenLandscape, - int idWhenOff) { - int stringID; + private String getAccessibilityString(boolean locked) { if (locked) { - stringID = isCurrentOrientationLockPortrait(mController, mContext) ? idWhenPortrait - : idWhenLandscape; + return mContext.getString(R.string.accessibility_quick_settings_rotation) + "," + + mContext.getString(R.string.accessibility_quick_settings_rotation_value, + isCurrentOrientationLockPortrait(mController, mContext) + ? mContext.getString( + R.string.quick_settings_rotation_locked_portrait_label) + : mContext.getString( + R.string.quick_settings_rotation_locked_landscape_label)); + } else { - stringID = idWhenOff; + return mContext.getString(R.string.accessibility_quick_settings_rotation); } - return mContext.getString(stringID); } @Override protected String composeChangeAnnouncement() { - return getAccessibilityString(mState.value, - R.string.accessibility_rotation_lock_on_portrait_changed, - R.string.accessibility_rotation_lock_on_landscape_changed, - R.string.accessibility_rotation_lock_off_changed); + return getAccessibilityString(mState.value); } private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java index c72bbf394beb..661212c13d7a 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java @@ -24,6 +24,8 @@ import android.provider.Settings; import android.util.Log; import android.view.View; import android.view.ViewGroup; +import android.widget.Button; +import android.widget.Switch; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; @@ -101,8 +103,8 @@ public class WifiTile extends QSTile { protected void handleSecondaryClick() { // Secondary clicks are header clicks, just toggle. mState.copyTo(mStateBeforeClick); - MetricsLogger.action(mContext, getMetricsCategory(), !mState.enabled); - mController.setWifiEnabled(!mState.enabled); + MetricsLogger.action(mContext, getMetricsCategory(), !mState.value); + mController.setWifiEnabled(!mState.value); } @Override @@ -111,9 +113,9 @@ public class WifiTile extends QSTile { mHost.startActivityDismissingKeyguard(new Intent(Settings.ACTION_WIFI_SETTINGS)); return; } - if (!mState.enabled) { + if (!mState.value) { mController.setWifiEnabled(true); - mState.enabled = true; + mState.value = true; } showDetail(true); } @@ -133,43 +135,58 @@ public class WifiTile extends QSTile { boolean wifiConnected = cb.enabled && (cb.wifiSignalIconId > 0) && (cb.enabledDesc != null); boolean wifiNotConnected = (cb.wifiSignalIconId > 0) && (cb.enabledDesc == null); - boolean enabledChanging = state.enabled != cb.enabled; + boolean enabledChanging = state.value != cb.enabled; if (enabledChanging) { mDetailAdapter.setItemsVisible(cb.enabled); fireToggleStateChanged(cb.enabled); } - state.enabled = cb.enabled; + state.value = cb.enabled; state.connected = wifiConnected; state.activityIn = cb.enabled && cb.activityIn; state.activityOut = cb.enabled && cb.activityOut; state.filter = true; - final String signalContentDescription; + final StringBuffer minimalContentDescription = new StringBuffer(); + final StringBuffer expandedContentDescription = new StringBuffer(); final Resources r = mContext.getResources(); - if (!state.enabled) { + if (!state.value) { state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disabled); state.label = r.getString(R.string.quick_settings_wifi_label); - signalContentDescription = r.getString(R.string.accessibility_wifi_off); } else if (wifiConnected) { state.icon = ResourceIcon.get(cb.wifiSignalIconId); state.label = removeDoubleQuotes(cb.enabledDesc); - signalContentDescription = cb.wifiSignalContentDescription; } else if (wifiNotConnected) { state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_disconnected); state.label = r.getString(R.string.quick_settings_wifi_label); - signalContentDescription = r.getString(R.string.accessibility_no_wifi); } else { state.icon = ResourceIcon.get(R.drawable.ic_qs_wifi_no_network); state.label = r.getString(R.string.quick_settings_wifi_label); - signalContentDescription = r.getString(R.string.accessibility_wifi_off); } - state.contentDescription = mContext.getString( - R.string.accessibility_quick_settings_wifi, - signalContentDescription); + minimalContentDescription.append( + mContext.getString(R.string.quick_settings_wifi_label)).append(","); + if (state.value) { + expandedContentDescription.append( + r.getString(R.string.quick_settings_wifi_on_label)).append(","); + if (wifiConnected) { + minimalContentDescription.append(cb.wifiSignalContentDescription).append(","); + minimalContentDescription.append(removeDoubleQuotes(cb.enabledDesc)); + expandedContentDescription.append(cb.wifiSignalContentDescription).append(","); + expandedContentDescription.append(removeDoubleQuotes(cb.enabledDesc)); + } + } else { + expandedContentDescription.append( + r.getString(R.string.quick_settings_wifi_off_label)); + } + state.minimalContentDescription = minimalContentDescription; + expandedContentDescription.append(",").append( + r.getString(R.string.accessibility_quick_settings_open_settings, getTileLabel())); + state.contentDescription = expandedContentDescription; CharSequence wifiName = state.label; if (state.connected) { wifiName = r.getString(R.string.accessibility_wifi_name, state.label); } state.dualLabelContentDescription = wifiName; + state.expandedAccessibilityClassName = Button.class.getName(); + state.minimalAccessibilityClassName = Switch.class.getName(); } @Override @@ -179,12 +196,12 @@ public class WifiTile extends QSTile { @Override protected boolean shouldAnnouncementBeDelayed() { - return mStateBeforeClick.enabled == mState.enabled; + return mStateBeforeClick.value == mState.value; } @Override protected String composeChangeAnnouncement() { - if (mState.enabled) { + if (mState.value) { return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_on); } else { return mContext.getString(R.string.accessibility_quick_settings_wifi_changed_off); @@ -263,7 +280,7 @@ public class WifiTile extends QSTile { @Override public Boolean getToggleState() { - return mState.enabled; + return mState.value; } @Override @@ -291,7 +308,7 @@ public class WifiTile extends QSTile { mItems.setEmptyState(R.drawable.ic_qs_wifi_detail_empty, R.string.quick_settings_wifi_detail_empty_text); updateItems(); - setItemsVisible(mState.enabled); + setItemsVisible(mState.value); return mItems; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java index 2c5f7d5b1582..459e8ece569e 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java @@ -18,6 +18,8 @@ package com.android.systemui.qs.tiles; import android.content.Intent; import android.provider.Settings; +import android.widget.Switch; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsProto.MetricsEvent; import com.android.systemui.R; @@ -104,6 +106,8 @@ public class WorkModeTile extends QSTile implements state.contentDescription = mContext.getString( R.string.accessibility_quick_settings_work_mode_off); } + state.minimalAccessibilityClassName = state.expandedAccessibilityClassName + = Switch.class.getName(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java index 3fdc35c90586..a295cfacbbcf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java @@ -34,6 +34,7 @@ public class ExpandableIndicator extends ImageView { super.onFinishInflate(); final int res = getDrawableResourceId(mExpanded); setImageResource(res); + setContentDescription(getContentDescription(mExpanded)); } public void setExpanded(boolean expanded) { @@ -46,6 +47,7 @@ public class ExpandableIndicator extends ImageView { setImageDrawable(avd); avd.forceAnimationOnUI(); avd.start(); + setContentDescription(getContentDescription(expanded)); } /** Whether the icons are using the default direction or the opposite */ @@ -62,4 +64,9 @@ public class ExpandableIndicator extends ImageView { : R.drawable.ic_volume_collapse_animation; } } + + private String getContentDescription(boolean expanded) { + return expanded ? mContext.getString(R.string.accessibility_quick_settings_collapse) + : mContext.getString(R.string.accessibility_quick_settings_expand); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java index 6698d13ab123..0186d3404637 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitch.java @@ -25,6 +25,9 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.Button; import android.widget.FrameLayout; import com.android.systemui.R; @@ -150,24 +153,11 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener } String text = null; - if (isClickable()) { - if (mUserManager.isUserSwitcherEnabled()) { - if (TextUtils.isEmpty(currentUser)) { - text = mContext.getString(R.string.accessibility_multi_user_switch_switcher); - } else { - text = mContext.getString( - R.string.accessibility_multi_user_switch_switcher_with_current, - currentUser); - } - } else { - text = mContext.getString(R.string.accessibility_multi_user_switch_quick_contact); - } - } else { - if (!TextUtils.isEmpty(currentUser)) { - text = mContext.getString( - R.string.accessibility_multi_user_switch_inactive, - currentUser); - } + + if (!TextUtils.isEmpty(currentUser)) { + text = mContext.getString( + R.string.accessibility_quick_settings_user, + currentUser); } if (!TextUtils.equals(getContentDescription(), text)) { @@ -176,6 +166,18 @@ public class MultiUserSwitch extends FrameLayout implements View.OnClickListener } @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + event.setClassName(Button.class.getName()); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(Button.class.getName()); + } + + @Override public boolean hasOverlappingRendering() { return false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java index 4a8d27feeb36..e32d51a234af 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java @@ -224,7 +224,12 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements public void onNextAlarmChanged(AlarmManager.AlarmClockInfo nextAlarm) { mNextAlarm = nextAlarm; if (nextAlarm != null) { - mAlarmStatus.setText(KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm)); + String alarmString = KeyguardStatusView.formatNextAlarm(getContext(), nextAlarm); + mAlarmStatus.setText(alarmString); + mAlarmStatus.setContentDescription(mContext.getString( + R.string.accessibility_quick_settings_alarm, alarmString)); + mAlarmStatusCollapsed.setContentDescription(mContext.getString( + R.string.accessibility_quick_settings_alarm, alarmString)); } if (mAlarmShowing != (nextAlarm != null)) { mAlarmShowing = nextAlarm != null; @@ -281,6 +286,7 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements public void updateEverything() { updateDateTimePosition(); updateVisibilities(); + setClickable(false); } protected void updateVisibilities() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index a6ed04f46c95..b890a3006e56 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -30,6 +30,8 @@ import com.android.settingslib.wifi.WifiStatusTracker; import com.android.systemui.statusbar.policy.NetworkController.IconState; import com.android.systemui.statusbar.policy.NetworkController.SignalCallback; +import com.android.systemui.R; + import java.util.Objects; @@ -80,6 +82,10 @@ public class WifiSignalController extends String wifiDesc = wifiVisible ? mCurrentState.ssid : null; boolean ssidPresent = wifiVisible && mCurrentState.ssid != null; String contentDescription = getStringIfExists(getContentDescription()); + if (mCurrentState.inetCondition == 0) { + contentDescription += + ("," + mContext.getString(R.string.accessibility_quick_settings_no_internet)); + } IconState statusIcon = new IconState(wifiVisible, getCurrentIconId(), contentDescription); IconState qsIcon = new IconState(mCurrentState.connected, getQsCurrentIconId(), -- 2.11.0