1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.volume;
18 
19 import static android.media.AudioManager.RINGER_MODE_NORMAL;
20 import static android.media.AudioManager.RINGER_MODE_SILENT;
21 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
22 
23 import static com.android.systemui.flags.Flags.ONE_WAY_HAPTICS_API_MIGRATION;
24 import static com.android.systemui.volume.Events.DISMISS_REASON_UNKNOWN;
25 import static com.android.systemui.volume.Events.SHOW_REASON_UNKNOWN;
26 import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
27 
28 import static junit.framework.Assert.assertEquals;
29 import static junit.framework.Assert.assertNotSame;
30 import static junit.framework.Assert.assertTrue;
31 
32 import static org.junit.Assume.assumeNotNull;
33 import static org.mockito.ArgumentMatchers.any;
34 import static org.mockito.ArgumentMatchers.eq;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.reset;
37 import static org.mockito.Mockito.times;
38 import static org.mockito.Mockito.verify;
39 import static org.mockito.Mockito.when;
40 
41 import android.app.KeyguardManager;
42 import android.content.res.Configuration;
43 import android.media.AudioManager;
44 import android.os.SystemClock;
45 import android.testing.AndroidTestingRunner;
46 import android.testing.TestableLooper;
47 import android.util.Log;
48 import android.view.Gravity;
49 import android.view.InputDevice;
50 import android.view.MotionEvent;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.accessibility.AccessibilityManager;
54 
55 import androidx.test.filters.SmallTest;
56 
57 import com.android.internal.jank.InteractionJankMonitor;
58 import com.android.systemui.Prefs;
59 import com.android.systemui.R;
60 import com.android.systemui.SysuiTestCase;
61 import com.android.systemui.animation.AnimatorTestRule;
62 import com.android.systemui.dump.DumpManager;
63 import com.android.systemui.flags.FakeFeatureFlags;
64 import com.android.systemui.media.dialog.MediaOutputDialogFactory;
65 import com.android.systemui.plugins.ActivityStarter;
66 import com.android.systemui.plugins.VolumeDialogController;
67 import com.android.systemui.plugins.VolumeDialogController.State;
68 import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
69 import com.android.systemui.statusbar.policy.ConfigurationController;
70 import com.android.systemui.statusbar.policy.DevicePostureController;
71 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
72 import com.android.systemui.statusbar.policy.FakeConfigurationController;
73 
74 import org.junit.After;
75 import org.junit.Before;
76 import org.junit.Rule;
77 import org.junit.Test;
78 import org.junit.runner.RunWith;
79 import org.mockito.ArgumentCaptor;
80 import org.mockito.Mock;
81 import org.mockito.Mockito;
82 import org.mockito.MockitoAnnotations;
83 
84 import java.util.function.Predicate;
85 
86 @SmallTest
87 @RunWith(AndroidTestingRunner.class)
88 @TestableLooper.RunWithLooper(setAsMainLooper = true)
89 public class VolumeDialogImplTest extends SysuiTestCase {
90     VolumeDialogImpl mDialog;
91     View mActiveRinger;
92     View mDrawerContainer;
93     View mDrawerVibrate;
94     View mDrawerMute;
95     View mDrawerNormal;
96     CaptionsToggleImageButton mODICaptionsIcon;
97 
98     private TestableLooper mTestableLooper;
99     private ConfigurationController mConfigurationController;
100     private int mOriginalOrientation;
101 
102     private static final String TAG = "VolumeDialogImplTest";
103 
104     @Mock
105     VolumeDialogController mVolumeDialogController;
106     @Mock
107     KeyguardManager mKeyguard;
108     @Mock
109     AccessibilityManagerWrapper mAccessibilityMgr;
110     @Mock
111     DeviceProvisionedController mDeviceProvisionedController;
112     @Mock
113     MediaOutputDialogFactory mMediaOutputDialogFactory;
114     @Mock
115     VolumePanelFactory mVolumePanelFactory;
116     @Mock
117     ActivityStarter mActivityStarter;
118     @Mock
119     InteractionJankMonitor mInteractionJankMonitor;
120     @Mock
121     private DumpManager mDumpManager;
122     @Mock CsdWarningDialog mCsdWarningDialog;
123     @Mock
124     DevicePostureController mPostureController;
125 
126     private final CsdWarningDialog.Factory mCsdWarningDialogFactory =
127             new CsdWarningDialog.Factory() {
128         @Override
129         public CsdWarningDialog create(int warningType, Runnable onCleanup) {
130             return mCsdWarningDialog;
131         }
132     };
133 
134     private FakeFeatureFlags mFeatureFlags;
135     private int mLongestHideShowAnimationDuration = 250;
136 
137     @Rule
138     public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule();
139 
140     @Before
setup()141     public void setup() throws Exception {
142         MockitoAnnotations.initMocks(this);
143 
144         getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
145 
146         mTestableLooper = TestableLooper.get(this);
147         allowTestableLooperAsMainThread();
148 
149         when(mPostureController.getDevicePosture())
150                 .thenReturn(DevicePostureController.DEVICE_POSTURE_CLOSED);
151 
152         int hideDialogDuration = mContext.getResources()
153                 .getInteger(R.integer.config_dialogHideAnimationDurationMs);
154         int showDialogDuration = mContext.getResources()
155                 .getInteger(R.integer.config_dialogShowAnimationDurationMs);
156 
157         mLongestHideShowAnimationDuration = Math.max(hideDialogDuration, showDialogDuration);
158 
159         mOriginalOrientation = mContext.getResources().getConfiguration().orientation;
160 
161         mConfigurationController = new FakeConfigurationController();
162 
163         mFeatureFlags = new FakeFeatureFlags();
164 
165         mDialog = new VolumeDialogImpl(
166                 getContext(),
167                 mVolumeDialogController,
168                 mAccessibilityMgr,
169                 mDeviceProvisionedController,
170                 mConfigurationController,
171                 mMediaOutputDialogFactory,
172                 mVolumePanelFactory,
173                 mActivityStarter,
174                 mInteractionJankMonitor,
175                 false,
176                 mCsdWarningDialogFactory,
177                 mPostureController,
178                 mTestableLooper.getLooper(),
179                 mDumpManager,
180                 mFeatureFlags);
181         mDialog.init(0, null);
182         State state = createShellState();
183         mDialog.onStateChangedH(state);
184 
185         mActiveRinger = mDialog.getDialogView().findViewById(
186                 R.id.volume_new_ringer_active_icon_container);
187         mDrawerContainer = mDialog.getDialogView().findViewById(R.id.volume_drawer_container);
188         mDrawerVibrate = mDrawerContainer.findViewById(R.id.volume_drawer_vibrate);
189         mDrawerMute = mDrawerContainer.findViewById(R.id.volume_drawer_mute);
190         mDrawerNormal = mDrawerContainer.findViewById(R.id.volume_drawer_normal);
191         mODICaptionsIcon = mDialog.getDialogView().findViewById(R.id.odi_captions_icon);
192 
193         Prefs.putInt(mContext,
194                 Prefs.Key.SEEN_RINGER_GUIDANCE_COUNT,
195                 VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
196 
197         Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
198     }
199 
createShellState()200     private State createShellState() {
201         State state = new VolumeDialogController.State();
202         for (int i = AudioManager.STREAM_VOICE_CALL; i <= AudioManager.STREAM_ACCESSIBILITY; i++) {
203             VolumeDialogController.StreamState ss = new VolumeDialogController.StreamState();
204             ss.name = STREAMS.get(i);
205             ss.level = 1;
206             state.states.append(i, ss);
207         }
208         return state;
209     }
210 
navigateViews(View view, Predicate<View> condition)211     private void navigateViews(View view, Predicate<View> condition) {
212         if (view instanceof ViewGroup) {
213             ViewGroup viewGroup = (ViewGroup) view;
214             for (int i = 0; i < viewGroup.getChildCount(); i++) {
215                 navigateViews(viewGroup.getChildAt(i), condition);
216             }
217         } else {
218             String resourceName = null;
219             try {
220                 resourceName = getContext().getResources().getResourceName(view.getId());
221             } catch (Exception e) {}
222             assertTrue("View " + resourceName != null ? resourceName : view.getId()
223                     + " failed test", condition.test(view));
224         }
225     }
226 
227     @Test
testComputeTimeout()228     public void testComputeTimeout() {
229         Mockito.reset(mAccessibilityMgr);
230         mDialog.rescheduleTimeoutH();
231         verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
232                 VolumeDialogImpl.DIALOG_TIMEOUT_MILLIS,
233                 AccessibilityManager.FLAG_CONTENT_CONTROLS);
234     }
235 
236     @Test
testComputeTimeout_tooltip()237     public void testComputeTimeout_tooltip() {
238         Mockito.reset(mAccessibilityMgr);
239         mDialog.showCaptionsTooltip();
240         verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
241                 VolumeDialogImpl.DIALOG_ODI_CAPTIONS_TOOLTIP_TIMEOUT_MILLIS,
242                 AccessibilityManager.FLAG_CONTENT_CONTROLS
243                 | AccessibilityManager.FLAG_CONTENT_TEXT);
244     }
245 
246     @Test
testComputeTimeout_withHovering()247     public void testComputeTimeout_withHovering() {
248         Mockito.reset(mAccessibilityMgr);
249         View dialog = mDialog.getDialogView();
250         long uptimeMillis = SystemClock.uptimeMillis();
251         MotionEvent event = MotionEvent.obtain(uptimeMillis, uptimeMillis,
252                 MotionEvent.ACTION_HOVER_ENTER, 0, 0, 0);
253         event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
254         dialog.dispatchGenericMotionEvent(event);
255         event.recycle();
256         verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
257                 VolumeDialogImpl.DIALOG_HOVERING_TIMEOUT_MILLIS,
258                 AccessibilityManager.FLAG_CONTENT_CONTROLS);
259     }
260 
261     @Test
testComputeTimeout_withSafetyWarningOn()262     public void testComputeTimeout_withSafetyWarningOn() {
263         Mockito.reset(mAccessibilityMgr);
264         ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture =
265                 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
266         verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any());
267         VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();
268 
269         callbacks.onShowSafetyWarning(AudioManager.FLAG_SHOW_UI);
270         verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
271                 VolumeDialogImpl.DIALOG_SAFETYWARNING_TIMEOUT_MILLIS,
272                 AccessibilityManager.FLAG_CONTENT_TEXT
273                         | AccessibilityManager.FLAG_CONTENT_CONTROLS);
274     }
275 
276     @Test
testComputeTimeout_standard()277     public void testComputeTimeout_standard() {
278         Mockito.reset(mAccessibilityMgr);
279         mDialog.tryToRemoveCaptionsTooltip();
280         mDialog.rescheduleTimeoutH();
281         verify(mAccessibilityMgr).getRecommendedTimeoutMillis(
282                 VolumeDialogImpl.DIALOG_TIMEOUT_MILLIS,
283                 AccessibilityManager.FLAG_CONTENT_CONTROLS);
284     }
285 
286     @Test
testVibrateOnRingerChangedToVibrate()287     public void testVibrateOnRingerChangedToVibrate() {
288         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
289         final State initialSilentState = new State();
290         initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
291 
292         final State vibrateState = new State();
293         vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
294 
295         // change ringer to silent
296         mDialog.onStateChangedH(initialSilentState);
297 
298         // expected: shouldn't call vibrate yet
299         verify(mVolumeDialogController, never()).vibrate(any());
300 
301         // changed ringer to vibrate
302         mDialog.onStateChangedH(vibrateState);
303 
304         // expected: vibrate device
305         verify(mVolumeDialogController).vibrate(any());
306     }
307 
308     @Test
testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On()309     public void testControllerDoesNotVibrateOnRingerChangedToVibrate_OnewayAPI_On() {
310         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
311         final State initialSilentState = new State();
312         initialSilentState.ringerModeInternal = AudioManager.RINGER_MODE_SILENT;
313 
314         final State vibrateState = new State();
315         vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
316 
317         // change ringer to silent
318         mDialog.onStateChangedH(initialSilentState);
319 
320         // expected: shouldn't call vibrate yet
321         verify(mVolumeDialogController, never()).vibrate(any());
322 
323         // changed ringer to vibrate
324         mDialog.onStateChangedH(vibrateState);
325 
326         // expected: vibrate method of controller is not used
327         verify(mVolumeDialogController, never()).vibrate(any());
328     }
329 
330     @Test
testNoVibrateOnRingerInitialization()331     public void testNoVibrateOnRingerInitialization() {
332         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
333         final State initialUnsetState = new State();
334         initialUnsetState.ringerModeInternal = -1;
335 
336         // ringer not initialized yet:
337         mDialog.onStateChangedH(initialUnsetState);
338 
339         final State vibrateState = new State();
340         vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
341 
342         // changed ringer to vibrate
343         mDialog.onStateChangedH(vibrateState);
344 
345         // shouldn't call vibrate
346         verify(mVolumeDialogController, never()).vibrate(any());
347     }
348 
349     @Test
testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On()350     public void testControllerDoesNotVibrateOnRingerInitialization_OnewayAPI_On() {
351         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
352         final State initialUnsetState = new State();
353         initialUnsetState.ringerModeInternal = -1;
354 
355         // ringer not initialized yet:
356         mDialog.onStateChangedH(initialUnsetState);
357 
358         final State vibrateState = new State();
359         vibrateState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
360 
361         // changed ringer to vibrate
362         mDialog.onStateChangedH(vibrateState);
363 
364         // shouldn't call vibrate on the controller either
365         verify(mVolumeDialogController, never()).vibrate(any());
366     }
367 
368     @Test
testSelectVibrateFromDrawer()369     public void testSelectVibrateFromDrawer() {
370         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
371         final State initialUnsetState = new State();
372         initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
373         mDialog.onStateChangedH(initialUnsetState);
374 
375         mActiveRinger.performClick();
376         mDrawerVibrate.performClick();
377 
378         // Make sure we've actually changed the ringer mode.
379         verify(mVolumeDialogController, times(1)).setRingerMode(
380                 AudioManager.RINGER_MODE_VIBRATE, false);
381     }
382 
383     @Test
testSelectVibrateFromDrawer_OnewayAPI_On()384     public void testSelectVibrateFromDrawer_OnewayAPI_On() {
385         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
386         final State initialUnsetState = new State();
387         initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
388         mDialog.onStateChangedH(initialUnsetState);
389 
390         mActiveRinger.performClick();
391         mDrawerVibrate.performClick();
392 
393         // Make sure we've actually changed the ringer mode.
394         verify(mVolumeDialogController, times(1)).setRingerMode(
395                 AudioManager.RINGER_MODE_VIBRATE, false);
396     }
397 
398     @Test
testSelectMuteFromDrawer()399     public void testSelectMuteFromDrawer() {
400         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
401         final State initialUnsetState = new State();
402         initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_NORMAL;
403         mDialog.onStateChangedH(initialUnsetState);
404 
405         mActiveRinger.performClick();
406         mDrawerMute.performClick();
407 
408         // Make sure we've actually changed the ringer mode.
409         verify(mVolumeDialogController, times(1)).setRingerMode(
410                 AudioManager.RINGER_MODE_SILENT, false);
411     }
412 
413     @Test
testSelectMuteFromDrawer_OnewayAPI_On()414     public void testSelectMuteFromDrawer_OnewayAPI_On() {
415         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
416         final State initialUnsetState = new State();
417         initialUnsetState.ringerModeInternal = RINGER_MODE_NORMAL;
418         mDialog.onStateChangedH(initialUnsetState);
419 
420         mActiveRinger.performClick();
421         mDrawerMute.performClick();
422 
423         // Make sure we've actually changed the ringer mode.
424         verify(mVolumeDialogController, times(1)).setRingerMode(
425                 AudioManager.RINGER_MODE_SILENT, false);
426     }
427 
428     @Test
testSelectNormalFromDrawer()429     public void testSelectNormalFromDrawer() {
430         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, false);
431         final State initialUnsetState = new State();
432         initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
433         mDialog.onStateChangedH(initialUnsetState);
434 
435         mActiveRinger.performClick();
436         mDrawerNormal.performClick();
437 
438         // Make sure we've actually changed the ringer mode.
439         verify(mVolumeDialogController, times(1)).setRingerMode(
440                 AudioManager.RINGER_MODE_NORMAL, false);
441     }
442 
443     @Test
testSelectNormalFromDrawer_OnewayAPI_On()444     public void testSelectNormalFromDrawer_OnewayAPI_On() {
445         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
446         final State initialUnsetState = new State();
447         initialUnsetState.ringerModeInternal = AudioManager.RINGER_MODE_VIBRATE;
448         mDialog.onStateChangedH(initialUnsetState);
449 
450         mActiveRinger.performClick();
451         mDrawerNormal.performClick();
452 
453         // Make sure we've actually changed the ringer mode.
454         verify(mVolumeDialogController, times(1)).setRingerMode(
455                 RINGER_MODE_NORMAL, false);
456     }
457 
458     /**
459      * Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
460      * API does not exist. So we do the next best thing; we check the cached icon id.
461      */
462     @Test
notificationVolumeSeparated_theRingerIconChangesToSpeakerIcon()463     public void notificationVolumeSeparated_theRingerIconChangesToSpeakerIcon() {
464         // already separated. assert icon is new based on res id
465         assertEquals(mDialog.mVolumeRingerIconDrawableId,
466                 R.drawable.ic_speaker_on);
467         assertEquals(mDialog.mVolumeRingerMuteIconDrawableId,
468                 R.drawable.ic_speaker_mute);
469     }
470 
471     @Test
testDialogDismissAnimation_notifyVisibleIsNotCalledBeforeAnimation()472     public void testDialogDismissAnimation_notifyVisibleIsNotCalledBeforeAnimation() {
473         mDialog.dismissH(DISMISS_REASON_UNKNOWN);
474         // notifyVisible(false) should not be called immediately but only after the dismiss
475         // animation has ended.
476         verify(mVolumeDialogController, times(0)).notifyVisible(false);
477         mDialog.getDialogView().animate().cancel();
478     }
479 
480     @Test
showCsdWarning_dialogShown()481     public void showCsdWarning_dialogShown() {
482         mDialog.showCsdWarningH(AudioManager.CSD_WARNING_DOSE_REACHED_1X,
483                 CsdWarningDialog.NO_ACTION_TIMEOUT_MS);
484 
485         verify(mCsdWarningDialog).show();
486     }
487 
488     @Test
ifPortraitHalfOpen_drawVerticallyTop()489     public void ifPortraitHalfOpen_drawVerticallyTop() {
490         mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
491         mTestableLooper.processAllMessages(); // let dismiss() finish
492 
493         setOrientation(Configuration.ORIENTATION_PORTRAIT);
494 
495         // Call show() to trigger layout updates before verifying position
496         mDialog.show(SHOW_REASON_UNKNOWN);
497         mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
498 
499         int gravity = mDialog.getWindowGravity();
500         assertEquals(Gravity.TOP, gravity & Gravity.VERTICAL_GRAVITY_MASK);
501     }
502 
503     @Test
ifPortraitAndOpen_drawCenterVertically()504     public void ifPortraitAndOpen_drawCenterVertically() {
505         mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_OPENED);
506         mTestableLooper.processAllMessages(); // let dismiss() finish
507 
508         setOrientation(Configuration.ORIENTATION_PORTRAIT);
509 
510         mDialog.show(SHOW_REASON_UNKNOWN);
511         mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
512 
513         int gravity = mDialog.getWindowGravity();
514         assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
515     }
516 
517     @Test
ifLandscapeAndHalfOpen_drawCenterVertically()518     public void ifLandscapeAndHalfOpen_drawCenterVertically() {
519         mDialog.onPostureChanged(DevicePostureController.DEVICE_POSTURE_HALF_OPENED);
520         mTestableLooper.processAllMessages(); // let dismiss() finish
521 
522         setOrientation(Configuration.ORIENTATION_LANDSCAPE);
523 
524         mDialog.show(SHOW_REASON_UNKNOWN);
525         mTestableLooper.processAllMessages(); // let show() finish before assessing its side-effect
526 
527         int gravity = mDialog.getWindowGravity();
528         assertEquals(Gravity.CENTER_VERTICAL, gravity & Gravity.VERTICAL_GRAVITY_MASK);
529     }
530 
531     @Test
dialogInit_addsPostureControllerCallback()532     public void dialogInit_addsPostureControllerCallback() {
533         // init is already called in setup
534         verify(mPostureController).addCallback(any());
535     }
536 
537     @Test
dialogDestroy_removesPostureControllerCallback()538     public void dialogDestroy_removesPostureControllerCallback() {
539         verify(mPostureController, never()).removeCallback(any());
540         mDialog.destroy();
541         verify(mPostureController).removeCallback(any());
542     }
543 
setOrientation(int orientation)544     private void setOrientation(int orientation) {
545         Configuration config = new Configuration();
546         config.orientation = orientation;
547         if (mConfigurationController != null) {
548             mConfigurationController.onConfigurationChanged(config);
549         }
550     }
551 
552     private enum RingerDrawerState {INIT, OPEN, CLOSE}
553 
554     @Test
ringerModeNormal_ringerContainerDescribesItsState()555     public void ringerModeNormal_ringerContainerDescribesItsState() {
556         assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.INIT);
557     }
558 
559     @Test
ringerModeSilent_ringerContainerDescribesItsState()560     public void ringerModeSilent_ringerContainerDescribesItsState() {
561         assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.INIT);
562     }
563 
564     @Test
ringerModeVibrate_ringerContainerDescribesItsState()565     public void ringerModeVibrate_ringerContainerDescribesItsState() {
566         assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.INIT);
567     }
568 
569     @Test
ringerModeNormal_openDrawer_ringerContainerDescribesItsState()570     public void ringerModeNormal_openDrawer_ringerContainerDescribesItsState() {
571         assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.OPEN);
572     }
573 
574     @Test
ringerModeSilent_openDrawer_ringerContainerDescribesItsState()575     public void ringerModeSilent_openDrawer_ringerContainerDescribesItsState() {
576         assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.OPEN);
577     }
578 
579     @Test
ringerModeVibrate_openDrawer_ringerContainerDescribesItsState()580     public void ringerModeVibrate_openDrawer_ringerContainerDescribesItsState() {
581         assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.OPEN);
582     }
583 
584     @Test
ringerModeNormal_closeDrawer_ringerContainerDescribesItsState()585     public void ringerModeNormal_closeDrawer_ringerContainerDescribesItsState() {
586         assertRingerContainerDescribesItsState(RINGER_MODE_NORMAL, RingerDrawerState.CLOSE);
587     }
588 
589     @Test
ringerModeSilent_closeDrawer_ringerContainerDescribesItsState()590     public void ringerModeSilent_closeDrawer_ringerContainerDescribesItsState() {
591         assertRingerContainerDescribesItsState(RINGER_MODE_SILENT, RingerDrawerState.CLOSE);
592     }
593 
594     @Test
ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState()595     public void ringerModeVibrate_closeDrawer_ringerContainerDescribesItsState() {
596         assertRingerContainerDescribesItsState(RINGER_MODE_VIBRATE, RingerDrawerState.CLOSE);
597     }
598 
599     @Test
testOnCaptionEnabledStateChanged_checkBeforeSwitchTrue_setCaptionsEnabledState()600     public void testOnCaptionEnabledStateChanged_checkBeforeSwitchTrue_setCaptionsEnabledState() {
601         ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture =
602                 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
603         verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any());
604         VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();
605 
606         callbacks.onCaptionEnabledStateChanged(true, true);
607         verify(mVolumeDialogController).setCaptionsEnabledState(eq(false));
608     }
609 
610     @Test
testOnCaptionEnabledStateChanged_checkBeforeSwitchFalse_getCaptionsEnabledTrue()611     public void testOnCaptionEnabledStateChanged_checkBeforeSwitchFalse_getCaptionsEnabledTrue() {
612         ArgumentCaptor<VolumeDialogController.Callbacks> controllerCallbackCapture =
613                 ArgumentCaptor.forClass(VolumeDialogController.Callbacks.class);
614         verify(mVolumeDialogController).addCallback(controllerCallbackCapture.capture(), any());
615         VolumeDialogController.Callbacks callbacks = controllerCallbackCapture.getValue();
616 
617         callbacks.onCaptionEnabledStateChanged(true, false);
618         assertTrue(mODICaptionsIcon.getCaptionsEnabled());
619     }
620 
621     /**
622      * The content description should include ringer state, and the correct one.
623      */
assertRingerContainerDescribesItsState(int ringerMode, RingerDrawerState drawerState)624     private void assertRingerContainerDescribesItsState(int ringerMode,
625             RingerDrawerState drawerState) {
626         State state = createShellState();
627         state.ringerModeInternal = ringerMode;
628         mFeatureFlags.set(ONE_WAY_HAPTICS_API_MIGRATION, true);
629         mDialog.onStateChangedH(state);
630 
631         mDialog.show(SHOW_REASON_UNKNOWN);
632 
633         if (drawerState != RingerDrawerState.INIT) {
634             // in both cases we first open the drawer
635             mDialog.toggleRingerDrawer(true);
636 
637             if (drawerState == RingerDrawerState.CLOSE) {
638                 mDialog.toggleRingerDrawer(false);
639             }
640         }
641 
642         String ringerContainerDescription = mDialog.getSelectedRingerContainerDescription();
643         assumeNotNull(ringerContainerDescription);
644 
645         String ringerDescription = mContext.getString(
646                 mDialog.getStringDescriptionResourceForRingerMode(ringerMode));
647 
648         if (drawerState == RingerDrawerState.OPEN) {
649             assertEquals(ringerDescription, ringerContainerDescription);
650         } else {
651             assertNotSame(ringerDescription, ringerContainerDescription);
652             assertTrue(ringerContainerDescription.startsWith(ringerDescription));
653         }
654     }
655 
656     @After
teardown()657     public void teardown() {
658         // Detailed logs to track down timeout issues in b/299491332
659         Log.d(TAG, "teardown: entered");
660         setOrientation(mOriginalOrientation);
661         Log.d(TAG, "teardown: after setOrientation");
662         mAnimatorTestRule.advanceTimeBy(mLongestHideShowAnimationDuration);
663         Log.d(TAG, "teardown: after advanceTimeBy");
664         mTestableLooper.moveTimeForward(mLongestHideShowAnimationDuration);
665         Log.d(TAG, "teardown: after moveTimeForward");
666         mTestableLooper.processAllMessages();
667         Log.d(TAG, "teardown: after processAllMessages");
668         reset(mPostureController);
669         Log.d(TAG, "teardown: after reset");
670         cleanUp(mDialog);
671         Log.d(TAG, "teardown: after cleanUp");
672     }
673 
cleanUp(VolumeDialogImpl dialog)674     private void cleanUp(VolumeDialogImpl dialog) {
675         if (dialog != null) {
676             dialog.clearInternalHandlerAfterTest();
677         }
678     }
679 
680 /*
681     @Test
682     public void testContentDescriptions() {
683         mDialog.show(SHOW_REASON_UNKNOWN);
684         ViewGroup dialog = mDialog.getDialogView();
685 
686         navigateViews(dialog, view -> {
687             if (view instanceof ImageView) {
688                 return !TextUtils.isEmpty(view.getContentDescription());
689             } else {
690                 return true;
691             }
692         });
693 
694         mDialog.dismiss(DISMISS_REASON_UNKNOWN);
695     }
696 
697     @Test
698     public void testNoDuplicationOfParentState() {
699         mDialog.show(SHOW_REASON_UNKNOWN);
700         ViewGroup dialog = mDialog.getDialogView();
701 
702         navigateViews(dialog, view -> !view.isDuplicateParentStateEnabled());
703 
704         mDialog.dismiss(DISMISS_REASON_UNKNOWN);
705     }
706 
707     @Test
708     public void testNoClickableViewGroups() {
709         mDialog.show(SHOW_REASON_UNKNOWN);
710         ViewGroup dialog = mDialog.getDialogView();
711 
712         navigateViews(dialog, view -> {
713             if (view instanceof ViewGroup) {
714                 return !view.isClickable();
715             } else {
716                 return true;
717             }
718         });
719 
720         mDialog.dismiss(DISMISS_REASON_UNKNOWN);
721     }
722 
723     @Test
724     public void testTristateToggle_withVibrator() {
725         when(mController.hasVibrator()).thenReturn(true);
726 
727         State state = createShellState();
728         state.ringerModeInternal = RINGER_MODE_NORMAL;
729         mDialog.onStateChangedH(state);
730 
731         mDialog.show(SHOW_REASON_UNKNOWN);
732         ViewGroup dialog = mDialog.getDialogView();
733 
734         // click once, verify updates to vibrate
735         dialog.findViewById(R.id.ringer_icon).performClick();
736         verify(mController, times(1)).setRingerMode(RINGER_MODE_VIBRATE, false);
737 
738         // fake the update back to the dialog with the new ringer mode
739         state = createShellState();
740         state.ringerModeInternal = RINGER_MODE_VIBRATE;
741         mDialog.onStateChangedH(state);
742 
743         // click once, verify updates to silent
744         dialog.findViewById(R.id.ringer_icon).performClick();
745         verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
746         verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
747 
748         // fake the update back to the dialog with the new ringer mode
749         state = createShellState();
750         state.states.get(STREAM_RING).level = 0;
751         state.ringerModeInternal = RINGER_MODE_SILENT;
752         mDialog.onStateChangedH(state);
753 
754         // click once, verify updates to normal
755         dialog.findViewById(R.id.ringer_icon).performClick();
756         verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
757         verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
758     }
759 
760     @Test
761     public void testTristateToggle_withoutVibrator() {
762         when(mController.hasVibrator()).thenReturn(false);
763 
764         State state = createShellState();
765         state.ringerModeInternal = RINGER_MODE_NORMAL;
766         mDialog.onStateChangedH(state);
767 
768         mDialog.show(SHOW_REASON_UNKNOWN);
769         ViewGroup dialog = mDialog.getDialogView();
770 
771         // click once, verify updates to silent
772         dialog.findViewById(R.id.ringer_icon).performClick();
773         verify(mController, times(1)).setRingerMode(RINGER_MODE_SILENT, false);
774         verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
775 
776         // fake the update back to the dialog with the new ringer mode
777         state = createShellState();
778         state.states.get(STREAM_RING).level = 0;
779         state.ringerModeInternal = RINGER_MODE_SILENT;
780         mDialog.onStateChangedH(state);
781 
782         // click once, verify updates to normal
783         dialog.findViewById(R.id.ringer_icon).performClick();
784         verify(mController, times(1)).setRingerMode(RINGER_MODE_NORMAL, false);
785         verify(mController, times(1)).setStreamVolume(STREAM_RING, 0);
786     }
787     */
788 }
789