From 561d3f4d09b8f810f59d0e5838dee9d50ec7fde4 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Fri, 26 Jan 2018 11:31:02 -0500 Subject: [PATCH] Add content description to volume button Change-Id: I910139816a5d57acc72cf803d421b00c21564e38 Fixes: 72458451 Test: runtest systemui --- .../SystemUI/res/layout/volume_dialog_row.xml | 1 + packages/SystemUI/res/values/strings.xml | 3 + .../policy/AccessibilityManagerWrapper.java | 27 +++++ .../volume/VolumeDialogControllerImpl.java | 2 +- .../systemui/volume/VolumeDialogImpl.java | 16 ++- .../systemui/volume/VolumeDialogImplTest.java | 114 ++++++++++++++++++ 6 files changed, 156 insertions(+), 7 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java diff --git a/packages/SystemUI/res/layout/volume_dialog_row.xml b/packages/SystemUI/res/layout/volume_dialog_row.xml index 1d1f95b446591..3e80085225e2f 100644 --- a/packages/SystemUI/res/layout/volume_dialog_row.xml +++ b/packages/SystemUI/res/layout/volume_dialog_row.xml @@ -61,6 +61,7 @@ android:layout_width="24dp" android:layout_height="24dp" android:background="?android:selectableItemBackgroundBorderless" + android:contentDescription="@string/accessibility_output_chooser" style="@style/VolumeButtons" android:layout_centerVertical="true" android:src="@drawable/ic_swap" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 9b6af43a1920b..72615ce690e5e 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1272,6 +1272,9 @@ Collapse + + Switch output device + Screen is pinned diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java index 6a573f593a924..d85e18c172525 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessibilityManagerWrapper.java @@ -14,10 +14,14 @@ package com.android.systemui.statusbar.policy; +import android.accessibilityservice.AccessibilityServiceInfo; import android.content.Context; +import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; +import java.util.List; + /** * For mocking because AccessibilityManager is final for some reason... */ @@ -39,4 +43,27 @@ public class AccessibilityManagerWrapper implements public void removeCallback(AccessibilityServicesStateChangeListener listener) { mAccessibilityManager.removeAccessibilityServicesStateChangeListener(listener); } + + public void addAccessibilityStateChangeListener( + AccessibilityManager.AccessibilityStateChangeListener listener) { + mAccessibilityManager.addAccessibilityStateChangeListener(listener); + } + + public void removeAccessibilityStateChangeListener( + AccessibilityManager.AccessibilityStateChangeListener listener) { + mAccessibilityManager.removeAccessibilityStateChangeListener(listener); + } + + public boolean isEnabled() { + return mAccessibilityManager.isEnabled(); + } + + public void sendAccessibilityEvent(AccessibilityEvent event) { + mAccessibilityManager.sendAccessibilityEvent(event); + } + + public List getEnabledAccessibilityServiceList( + int feedbackTypeFlags) { + return mAccessibilityManager.getEnabledAccessibilityServiceList(feedbackTypeFlags); + } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java index a166db51956cc..b031bdd71e775 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java @@ -76,7 +76,7 @@ public class VolumeDialogControllerImpl implements VolumeDialogController, Dumpa private static final int DYNAMIC_STREAM_START_INDEX = 100; private static final int VIBRATE_HINT_DURATION = 50; - private static final ArrayMap STREAMS = new ArrayMap<>(); + static final ArrayMap STREAMS = new ArrayMap<>(); static { STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm); STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco); diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 8cfdeeb67d98e..001a582297af5 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -63,7 +63,6 @@ import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener; import android.view.animation.DecelerateInterpolator; import android.widget.ImageButton; @@ -79,6 +78,7 @@ import com.android.systemui.plugins.VolumeDialog; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.plugins.VolumeDialogController.State; import com.android.systemui.plugins.VolumeDialogController.StreamState; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; import java.io.PrintWriter; import java.util.ArrayList; @@ -111,7 +111,7 @@ public class VolumeDialogImpl implements VolumeDialog { private ConfigurableTexts mConfigurableTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); private final KeyguardManager mKeyguard; - private final AccessibilityManager mAccessibilityMgr; + private final AccessibilityManagerWrapper mAccessibilityMgr; private final Object mSafetyWarningLock = new Object(); private final Object mOutputChooserLock = new Object(); private final Accessibility mAccessibility = new Accessibility(); @@ -134,8 +134,7 @@ public class VolumeDialogImpl implements VolumeDialog { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mController = Dependency.get(VolumeDialogController.class); mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); - mAccessibilityMgr = - (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); + mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class); mActiveSliderTint = ColorStateList.valueOf(Utils.getColorAccent(mContext)); mInactiveSliderTint = loadColorStateList(R.color.volume_slider_inactive); } @@ -231,6 +230,10 @@ public class VolumeDialogImpl implements VolumeDialog { initRingerH(); } + protected ViewGroup getDialogView() { + return mDialogView; + } + private ColorStateList loadColorStateList(int colorResId) { return ColorStateList.valueOf(mContext.getColor(colorResId)); } @@ -258,6 +261,7 @@ public class VolumeDialogImpl implements VolumeDialog { private void addRow(int stream, int iconRes, int iconMuteRes, boolean important, boolean defaultStream, boolean dynamic) { + if (D.BUG) Slog.d(TAG, "Adding row for stream " + stream); VolumeRow row = new VolumeRow(); initRow(row, stream, iconRes, iconMuteRes, important, defaultStream); int rowSize; @@ -621,7 +625,7 @@ public class VolumeDialogImpl implements VolumeDialog { } } - private void onStateChangedH(State state) { + protected void onStateChangedH(State state) { mState = state; mDynamic.clear(); // add any new dynamic rows @@ -894,7 +898,7 @@ public class VolumeDialogImpl implements VolumeDialog { return ss.remoteLabel; } try { - return mContext.getString(ss.name); + return mContext.getResources().getString(ss.name); } catch (Resources.NotFoundException e) { Slog.e(TAG, "Can't find translation for stream " + ss); return ""; diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java new file mode 100644 index 0000000000000..2d28c9f214fb8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2018 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.volume; + +import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN; +import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN; +import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS; + +import static junit.framework.Assert.assertTrue; + +import android.app.KeyguardManager; +import android.media.AudioManager; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.text.TextUtils; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.VolumeDialogController; +import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.function.Predicate; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper +public class VolumeDialogImplTest extends SysuiTestCase { + + VolumeDialogImpl mDialog; + + @Mock + VolumeDialogController mController; + + @Mock + KeyguardManager mKeyguard; + + @Mock + AccessibilityManagerWrapper mAccessibilityMgr; + + @Before + public void setup() throws Exception { + MockitoAnnotations.initMocks(this); + + mController = mDependency.injectMockDependency(VolumeDialogController.class); + mAccessibilityMgr = mDependency.injectMockDependency(AccessibilityManagerWrapper.class); + getContext().addMockSystemService(KeyguardManager.class, mKeyguard); + + mDialog = new VolumeDialogImpl(getContext()); + mDialog.init(0, null); + VolumeDialogController.State state = new VolumeDialogController.State(); + for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) { + VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState(); + ss.name = STREAMS.get(i); + state.states.append(i, ss); + } + mDialog.onStateChangedH(state); + } + + private void navigateViews(View view, Predicate condition) { + if (view instanceof ViewGroup) { + ViewGroup viewGroup = (ViewGroup) view; + for (int i = 0; i < viewGroup.getChildCount(); i++) { + navigateViews(viewGroup.getChildAt(i), condition); + } + } else { + String resourceName = null; + try { + resourceName = getContext().getResources().getResourceName(view.getId()); + } catch (Exception e) {} + assertTrue("View " + resourceName != null ? resourceName : view.getId() + + " failed test", condition.test(view)); + } + } + + @Test + public void testContentDescriptions() { + mDialog.show(SHOW_REASON_UNKNOWN); + ViewGroup dialog = mDialog.getDialogView(); + + navigateViews(dialog, view -> { + if (view instanceof ImageView) { + return !TextUtils.isEmpty(view.getContentDescription()); + } else { + return true; + } + }); + + mDialog.dismiss(DISMISS_REASON_UNKNOWN); + } + +}