Merge "Add content description to volume button"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9f2222f6ea
@@ -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"
|
||||
|
||||
@@ -1272,6 +1272,9 @@
|
||||
<!-- Content description for accessibility (not shown on the screen): volume dialog collapse button. [CHAR LIMIT=NONE] -->
|
||||
<string name="accessibility_volume_collapse">Collapse</string>
|
||||
|
||||
<!-- content description for audio output chooser [CHAR LIMIT=NONE]-->
|
||||
<string name="accessibility_output_chooser">Switch output device</string>
|
||||
|
||||
<!-- Screen pinning dialog title. -->
|
||||
<string name="screen_pinning_title">Screen is pinned</string>
|
||||
<!-- Screen pinning dialog description. -->
|
||||
|
||||
@@ -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<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
|
||||
int feedbackTypeFlags) {
|
||||
return mAccessibilityManager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Integer, Integer> STREAMS = new ArrayMap<>();
|
||||
static final ArrayMap<Integer, Integer> STREAMS = new ArrayMap<>();
|
||||
static {
|
||||
STREAMS.put(AudioSystem.STREAM_ALARM, R.string.stream_alarm);
|
||||
STREAMS.put(AudioSystem.STREAM_BLUETOOTH_SCO, R.string.stream_bluetooth_sco);
|
||||
|
||||
@@ -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 "";
|
||||
|
||||
@@ -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<View> 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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user