1 /*
2  * Copyright (C) 2017 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.statusbar.phone;
18 
19 import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE;
20 import static com.android.systemui.statusbar.phone.AutoTileManager.DEVICE_CONTROLS;
21 
22 import static org.junit.Assert.assertEquals;
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertNull;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 import static org.mockito.ArgumentMatchers.any;
28 import static org.mockito.ArgumentMatchers.eq;
29 import static org.mockito.ArgumentMatchers.isNotNull;
30 import static org.mockito.ArgumentMatchers.isNull;
31 import static org.mockito.Mockito.doReturn;
32 import static org.mockito.Mockito.inOrder;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.times;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.verifyNoMoreInteractions;
38 import static org.mockito.Mockito.when;
39 
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.pm.PackageManager;
43 import android.hardware.display.ColorDisplayManager;
44 import android.hardware.display.NightDisplayListener;
45 import android.os.Handler;
46 import android.os.UserHandle;
47 import android.testing.AndroidTestingRunner;
48 import android.testing.TestableLooper;
49 import android.testing.TestableLooper.RunWithLooper;
50 
51 import androidx.test.filters.SmallTest;
52 
53 import com.android.systemui.R;
54 import com.android.systemui.SysuiTestCase;
55 import com.android.systemui.dagger.NightDisplayListenerModule;
56 import com.android.systemui.plugins.qs.QSTile;
57 import com.android.systemui.qs.AutoAddTracker;
58 import com.android.systemui.qs.QSHost;
59 import com.android.systemui.qs.ReduceBrightColorsController;
60 import com.android.systemui.qs.SettingObserver;
61 import com.android.systemui.qs.external.CustomTile;
62 import com.android.systemui.statusbar.policy.CastController;
63 import com.android.systemui.statusbar.policy.CastController.CastDevice;
64 import com.android.systemui.statusbar.policy.DataSaverController;
65 import com.android.systemui.statusbar.policy.DeviceControlsController;
66 import com.android.systemui.statusbar.policy.HotspotController;
67 import com.android.systemui.statusbar.policy.SafetyController;
68 import com.android.systemui.statusbar.policy.WalletController;
69 import com.android.systemui.util.settings.FakeSettings;
70 import com.android.systemui.util.settings.SecureSettings;
71 
72 import org.junit.After;
73 import org.junit.Before;
74 import org.junit.Test;
75 import org.junit.runner.RunWith;
76 import org.mockito.Answers;
77 import org.mockito.ArgumentCaptor;
78 import org.mockito.InOrder;
79 import org.mockito.Mock;
80 import org.mockito.Mockito;
81 import org.mockito.MockitoAnnotations;
82 import org.mockito.Spy;
83 import org.mockito.stubbing.Answer;
84 
85 import java.util.ArrayList;
86 import java.util.Collections;
87 import java.util.List;
88 
89 import javax.inject.Named;
90 
91 @RunWith(AndroidTestingRunner.class)
92 @RunWithLooper
93 @SmallTest
94 public class AutoTileManagerTest extends SysuiTestCase {
95 
96     private static final String TEST_SETTING = "setting";
97     private static final String TEST_SPEC = "spec";
98     private static final String TEST_SETTING_COMPONENT = "setting_component";
99     private static final String TEST_COMPONENT = "test_pkg/test_cls";
100     private static final String TEST_CUSTOM_SPEC = "custom(" + TEST_COMPONENT + ")";
101     private static final String TEST_CUSTOM_SAFETY_CLASS = "safety_cls";
102     private static final String TEST_CUSTOM_SAFETY_PKG = "safety_pkg";
103     private static final String TEST_CUSTOM_SAFETY_SPEC = CustomTile.toSpec(new ComponentName(
104             TEST_CUSTOM_SAFETY_PKG, TEST_CUSTOM_SAFETY_CLASS));
105     private static final String SEPARATOR = AutoTileManager.SETTING_SEPARATOR;
106 
107     private static final int USER = 0;
108 
109     @Mock private QSHost mQsHost;
110     @Mock private AutoAddTracker mAutoAddTracker;
111     @Mock private CastController mCastController;
112     @Mock private HotspotController mHotspotController;
113     @Mock private DataSaverController mDataSaverController;
114     @Mock private ManagedProfileController mManagedProfileController;
115     @Mock private NightDisplayListener mNightDisplayListener;
116     @Mock(answer = Answers.RETURNS_SELF)
117     private NightDisplayListenerModule.Builder mNightDisplayListenerBuilder;
118     @Mock private ReduceBrightColorsController mReduceBrightColorsController;
119     @Mock private DeviceControlsController mDeviceControlsController;
120     @Mock private WalletController mWalletController;
121     @Mock private SafetyController mSafetyController;
122     @Mock(answer = Answers.RETURNS_SELF)
123     private AutoAddTracker.Builder mAutoAddTrackerBuilder;
124     @Mock private Context mUserContext;
125     @Spy private PackageManager mPackageManager;
126     private final boolean mIsReduceBrightColorsAvailable = true;
127 
128     private AutoTileManager mAutoTileManager; // under test
129 
130     private SecureSettings mSecureSettings;
131     private ManagedProfileController.Callback mManagedProfileCallback;
132 
133     @Before
setUp()134     public void setUp() throws Exception {
135         MockitoAnnotations.initMocks(this);
136         mSecureSettings = new FakeSettings();
137 
138         mContext.getOrCreateTestableResources().addOverride(
139                 R.array.config_quickSettingsAutoAdd,
140                 new String[] {
141                         TEST_SETTING + SEPARATOR + TEST_SPEC,
142                         TEST_SETTING_COMPONENT + SEPARATOR + TEST_CUSTOM_SPEC
143                 }
144         );
145         mContext.getOrCreateTestableResources().addOverride(
146                 com.android.internal.R.bool.config_nightDisplayAvailable, true);
147         mContext.getOrCreateTestableResources().addOverride(
148                 R.string.safety_quick_settings_tile_class, TEST_CUSTOM_SAFETY_CLASS);
149 
150         when(mAutoAddTrackerBuilder.build()).thenReturn(mAutoAddTracker);
151         when(mQsHost.getUserContext()).thenReturn(mUserContext);
152         when(mUserContext.getUser()).thenReturn(UserHandle.of(USER));
153         mPackageManager = Mockito.spy(mContext.getPackageManager());
154         when(mPackageManager.getPermissionControllerPackageName())
155                 .thenReturn(TEST_CUSTOM_SAFETY_PKG);
156         Context context = Mockito.spy(mContext);
157         when(context.getPackageManager()).thenReturn(mPackageManager);
158         when(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener);
159 
160         mAutoTileManager = createAutoTileManager(context);
161         mAutoTileManager.init();
162     }
163 
164     @After
tearDown()165     public void tearDown() {
166         mAutoTileManager.destroy();
167     }
168 
createAutoTileManager( Context context, AutoAddTracker.Builder autoAddTrackerBuilder, HotspotController hotspotController, DataSaverController dataSaverController, ManagedProfileController managedProfileController, NightDisplayListenerModule.Builder nightDisplayListenerBuilder, CastController castController, ReduceBrightColorsController reduceBrightColorsController, DeviceControlsController deviceControlsController, WalletController walletController, SafetyController safetyController, @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable)169     private AutoTileManager createAutoTileManager(
170             Context context,
171             AutoAddTracker.Builder autoAddTrackerBuilder,
172             HotspotController hotspotController,
173             DataSaverController dataSaverController,
174             ManagedProfileController managedProfileController,
175             NightDisplayListenerModule.Builder nightDisplayListenerBuilder,
176             CastController castController,
177             ReduceBrightColorsController reduceBrightColorsController,
178             DeviceControlsController deviceControlsController,
179             WalletController walletController,
180             SafetyController safetyController,
181             @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
182         return new AutoTileManager(context, autoAddTrackerBuilder, mQsHost,
183                 Handler.createAsync(TestableLooper.get(this).getLooper()),
184                 mSecureSettings,
185                 hotspotController,
186                 dataSaverController,
187                 managedProfileController,
188                 mNightDisplayListenerBuilder,
189                 castController,
190                 reduceBrightColorsController,
191                 deviceControlsController,
192                 walletController,
193                 safetyController,
194                 isReduceBrightColorsAvailable);
195     }
196 
createAutoTileManager(Context context)197     private AutoTileManager createAutoTileManager(Context context) {
198         return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
199                 mDataSaverController, mManagedProfileController, mNightDisplayListenerBuilder,
200                 mCastController, mReduceBrightColorsController, mDeviceControlsController,
201                 mWalletController, mSafetyController, mIsReduceBrightColorsAvailable);
202     }
203 
204     @Test
testCreatedAutoTileManagerIsNotInitialized()205     public void testCreatedAutoTileManagerIsNotInitialized() {
206         AutoAddTracker.Builder builder = mock(AutoAddTracker.Builder.class, Answers.RETURNS_SELF);
207         AutoAddTracker tracker = mock(AutoAddTracker.class);
208         when(builder.build()).thenReturn(tracker);
209         HotspotController hC = mock(HotspotController.class);
210         DataSaverController dSC = mock(DataSaverController.class);
211         ManagedProfileController mPC = mock(ManagedProfileController.class);
212         NightDisplayListenerModule.Builder nDSB = mock(NightDisplayListenerModule.Builder.class);
213         CastController cC = mock(CastController.class);
214         ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
215         DeviceControlsController dCC = mock(DeviceControlsController.class);
216         WalletController wC = mock(WalletController.class);
217         SafetyController sC = mock(SafetyController.class);
218 
219         AutoTileManager manager =
220                 createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDSB, cC, rBC,
221                         dCC, wC, sC, true);
222 
223         verify(tracker, never()).initialize();
224         verify(hC, never()).addCallback(any());
225         verify(dSC, never()).addCallback(any());
226         verify(mPC, never()).addCallback(any());
227         verifyNoMoreInteractions(nDSB);
228         verify(cC, never()).addCallback(any());
229         verify(rBC, never()).addCallback(any());
230         verify(dCC, never()).setCallback(any());
231         verify(wC, never()).getWalletPosition();
232         verify(sC, never()).addCallback(any());
233         assertNull(manager.getSecureSettingForKey(TEST_SETTING));
234         assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
235     }
236 
237     @Test
testChangeUserWhenNotInitializedThrows()238     public void testChangeUserWhenNotInitializedThrows() {
239         AutoTileManager manager = createAutoTileManager(mock(Context.class));
240 
241         try {
242             manager.changeUser(UserHandle.of(USER + 1));
243             fail();
244         } catch (Exception e) {
245             // This should throw and take this path
246         }
247     }
248 
249     @Test
testChangeUserCallbacksStoppedAndStarted()250     public void testChangeUserCallbacksStoppedAndStarted() throws Exception {
251         TestableLooper.get(this).runWithLooper(() ->
252                 mAutoTileManager.changeUser(UserHandle.of(USER + 1))
253         );
254 
255         InOrder inOrderHotspot = inOrder(mHotspotController);
256         inOrderHotspot.verify(mHotspotController).removeCallback(any());
257         inOrderHotspot.verify(mHotspotController).addCallback(any());
258 
259         InOrder inOrderDataSaver = inOrder(mDataSaverController);
260         inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
261         inOrderDataSaver.verify(mDataSaverController).addCallback(any());
262 
263         InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
264         inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
265         inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
266 
267         if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
268             InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
269             inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
270             inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
271         }
272 
273         InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
274         inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
275         inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
276 
277         InOrder inOrderCast = inOrder(mCastController);
278         inOrderCast.verify(mCastController).removeCallback(any());
279         inOrderCast.verify(mCastController).addCallback(any());
280 
281         InOrder inOrderDevices = inOrder(mDeviceControlsController);
282         inOrderDevices.verify(mDeviceControlsController).removeCallback();
283         inOrderDevices.verify(mDeviceControlsController).setCallback(any());
284 
285         verify(mWalletController, times(2)).getWalletPosition();
286 
287         InOrder inOrderSafety = inOrder(mSafetyController);
288         inOrderSafety.verify(mSafetyController).removeCallback(any());
289         inOrderSafety.verify(mSafetyController).addCallback(any());
290 
291         SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
292         assertEquals(USER + 1, setting.getCurrentUser());
293         assertTrue(setting.isListening());
294     }
295 
296     @Test
testChangeUserSomeCallbacksNotAdded()297     public void testChangeUserSomeCallbacksNotAdded() throws Exception {
298         when(mAutoAddTracker.isAdded("hotspot")).thenReturn(true);
299         when(mAutoAddTracker.isAdded("work")).thenReturn(true);
300         when(mAutoAddTracker.isAdded("cast")).thenReturn(true);
301         when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
302 
303         TestableLooper.get(this).runWithLooper(() ->
304                 mAutoTileManager.changeUser(UserHandle.of(USER + 1))
305         );
306 
307         verify(mAutoAddTracker).changeUser(UserHandle.of(USER + 1));
308 
309         InOrder inOrderHotspot = inOrder(mHotspotController);
310         inOrderHotspot.verify(mHotspotController).removeCallback(any());
311         inOrderHotspot.verify(mHotspotController, never()).addCallback(any());
312 
313         InOrder inOrderDataSaver = inOrder(mDataSaverController);
314         inOrderDataSaver.verify(mDataSaverController).removeCallback(any());
315         inOrderDataSaver.verify(mDataSaverController).addCallback(any());
316 
317         InOrder inOrderManagedProfile = inOrder(mManagedProfileController);
318         inOrderManagedProfile.verify(mManagedProfileController).removeCallback(any());
319         inOrderManagedProfile.verify(mManagedProfileController).addCallback(any());
320 
321         if (ColorDisplayManager.isNightDisplayAvailable(mContext)) {
322             InOrder inOrderNightDisplay = inOrder(mNightDisplayListener);
323             inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNull());
324             inOrderNightDisplay.verify(mNightDisplayListener).setCallback(isNotNull());
325         }
326 
327         InOrder inOrderReduceBrightColors = inOrder(mReduceBrightColorsController);
328         inOrderReduceBrightColors.verify(mReduceBrightColorsController).removeCallback(any());
329         inOrderReduceBrightColors.verify(mReduceBrightColorsController).addCallback(any());
330 
331         InOrder inOrderCast = inOrder(mCastController);
332         inOrderCast.verify(mCastController).removeCallback(any());
333         inOrderCast.verify(mCastController, never()).addCallback(any());
334 
335         InOrder inOrderDevices = inOrder(mDeviceControlsController);
336         inOrderDevices.verify(mDeviceControlsController).removeCallback();
337         inOrderDevices.verify(mDeviceControlsController).setCallback(any());
338 
339         verify(mWalletController, times(2)).getWalletPosition();
340 
341         InOrder inOrderSafety = inOrder(mSafetyController);
342         inOrderSafety.verify(mSafetyController).removeCallback(any());
343         inOrderSafety.verify(mSafetyController).addCallback(any());
344 
345         SettingObserver setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
346         assertEquals(USER + 1, setting.getCurrentUser());
347         assertFalse(setting.isListening());
348     }
349 
350     @Test
testGetCurrentUserId()351     public void testGetCurrentUserId() throws Exception {
352         assertEquals(USER, mAutoTileManager.getCurrentUserId());
353 
354         TestableLooper.get(this).runWithLooper(() ->
355                 mAutoTileManager.changeUser(UserHandle.of(USER + 100))
356         );
357 
358         assertEquals(USER + 100, mAutoTileManager.getCurrentUserId());
359     }
360 
361     @Test
nightTileAdded_whenActivated()362     public void nightTileAdded_whenActivated() {
363         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
364             return;
365         }
366         mAutoTileManager.mNightDisplayCallback.onActivated(true);
367         verify(mQsHost).addTile("night");
368     }
369 
370     @Test
nightTileNotAdded_whenDeactivated()371     public void nightTileNotAdded_whenDeactivated() {
372         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
373             return;
374         }
375         mAutoTileManager.mNightDisplayCallback.onActivated(false);
376         verify(mQsHost, never()).addTile("night");
377     }
378 
379     @Test
nightTileAdded_whenNightModeTwilight()380     public void nightTileAdded_whenNightModeTwilight() {
381         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
382             return;
383         }
384         mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
385                 ColorDisplayManager.AUTO_MODE_TWILIGHT);
386         verify(mQsHost).addTile("night");
387     }
388 
389     @Test
nightTileAdded_whenNightModeCustom()390     public void nightTileAdded_whenNightModeCustom() {
391         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
392             return;
393         }
394         mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
395                 ColorDisplayManager.AUTO_MODE_CUSTOM_TIME);
396         verify(mQsHost).addTile("night");
397     }
398 
399     @Test
nightTileNotAdded_whenNightModeDisabled()400     public void nightTileNotAdded_whenNightModeDisabled() {
401         if (!ColorDisplayManager.isNightDisplayAvailable(mContext)) {
402             return;
403         }
404         mAutoTileManager.mNightDisplayCallback.onAutoModeChanged(
405                 ColorDisplayManager.AUTO_MODE_DISABLED);
406         verify(mQsHost, never()).addTile("night");
407     }
408 
409     @Test
reduceBrightColorsTileAdded_whenActivated()410     public void reduceBrightColorsTileAdded_whenActivated() {
411         mAutoTileManager.mReduceBrightColorsCallback.onActivated(true);
412         verify(mQsHost).addTile("reduce_brightness");
413     }
414 
415     @Test
reduceBrightColorsTileNotAdded_whenDeactivated()416     public void reduceBrightColorsTileNotAdded_whenDeactivated() {
417         mAutoTileManager.mReduceBrightColorsCallback.onActivated(false);
418         verify(mQsHost, never()).addTile("reduce_brightness");
419     }
420 
buildFakeCastDevice(boolean isCasting)421     private static List<CastDevice> buildFakeCastDevice(boolean isCasting) {
422         CastDevice cd = new CastDevice();
423         cd.state = isCasting ? CastDevice.STATE_CONNECTED : CastDevice.STATE_DISCONNECTED;
424         return Collections.singletonList(cd);
425     }
426 
427     @Test
castTileAdded_whenDeviceIsCasting()428     public void castTileAdded_whenDeviceIsCasting() {
429         doReturn(buildFakeCastDevice(true)).when(mCastController).getCastDevices();
430         mAutoTileManager.mCastCallback.onCastDevicesChanged();
431         verify(mQsHost).addTile("cast");
432     }
433 
434     @Test
castTileNotAdded_whenDeviceIsNotCasting()435     public void castTileNotAdded_whenDeviceIsNotCasting() {
436         doReturn(buildFakeCastDevice(false)).when(mCastController).getCastDevices();
437         mAutoTileManager.mCastCallback.onCastDevicesChanged();
438         verify(mQsHost, never()).addTile("cast");
439     }
440 
441     @Test
testSettingTileAdded_onChanged()442     public void testSettingTileAdded_onChanged() {
443         changeValue(TEST_SETTING, 1);
444         verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
445         verify(mQsHost).addTile(TEST_SPEC);
446     }
447 
448     @Test
testSettingTileAddedComponentAtEnd_onChanged()449     public void testSettingTileAddedComponentAtEnd_onChanged() {
450         changeValue(TEST_SETTING_COMPONENT, 1);
451         verify(mAutoAddTracker).setTileAdded(TEST_CUSTOM_SPEC);
452         verify(mQsHost).addTile(ComponentName.unflattenFromString(TEST_COMPONENT)
453             , /* end */ true);
454     }
455 
456     @Test
testSettingTileAdded_onlyOnce()457     public void testSettingTileAdded_onlyOnce() {
458         changeValue(TEST_SETTING, 1);
459         changeValue(TEST_SETTING, 2);
460         verify(mAutoAddTracker).setTileAdded(TEST_SPEC);
461         verify(mQsHost).addTile(TEST_SPEC);
462     }
463 
464     @Test
testSettingTileNotAdded_onChangedTo0()465     public void testSettingTileNotAdded_onChangedTo0() {
466         changeValue(TEST_SETTING, 0);
467         verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
468         verify(mQsHost, never()).addTile(TEST_SPEC);
469     }
470 
471     @Test
testSettingTileNotAdded_ifPreviouslyAdded()472     public void testSettingTileNotAdded_ifPreviouslyAdded() {
473         when(mAutoAddTracker.isAdded(TEST_SPEC)).thenReturn(true);
474 
475         changeValue(TEST_SETTING, 1);
476         verify(mAutoAddTracker, never()).setTileAdded(TEST_SPEC);
477         verify(mQsHost, never()).addTile(TEST_SPEC);
478     }
479 
480     @Test
testSafetyTileNotAdded_ifPreviouslyAdded()481     public void testSafetyTileNotAdded_ifPreviouslyAdded() {
482         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
483         mAutoTileManager.init();
484         verify(mQsHost, times(1)).addTile(safetyComponent, true);
485         when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
486         mAutoTileManager.init();
487         verify(mQsHost, times(1)).addTile(safetyComponent, true);
488     }
489 
490     @Test
testSafetyTileAdded_onUserChange()491     public void testSafetyTileAdded_onUserChange() {
492         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
493         mAutoTileManager.init();
494         verify(mQsHost, times(1)).addTile(safetyComponent, true);
495         when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(false);
496         mAutoTileManager.changeUser(UserHandle.of(USER + 1));
497         verify(mQsHost, times(2)).addTile(safetyComponent, true);
498     }
499 
500     @Test
testSafetyTileRemoved_onSafetyCenterDisable()501     public void testSafetyTileRemoved_onSafetyCenterDisable() {
502         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
503         mAutoTileManager.init();
504         when(mAutoAddTracker.isAdded(TEST_CUSTOM_SAFETY_SPEC)).thenReturn(true);
505         mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
506         verify(mQsHost, times(1)).removeTile(TEST_CUSTOM_SAFETY_SPEC);
507     }
508 
509     @Test
testSafetyTileAdded_onSafetyCenterEnable()510     public void testSafetyTileAdded_onSafetyCenterEnable() {
511         ComponentName safetyComponent = CustomTile.getComponentFromSpec(TEST_CUSTOM_SAFETY_SPEC);
512         mAutoTileManager.init();
513         verify(mQsHost, times(1)).addTile(safetyComponent, true);
514         mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(false);
515         mAutoTileManager.mSafetyCallback.onSafetyCenterEnableChanged(true);
516         verify(mQsHost, times(2)).addTile(safetyComponent, true);
517     }
518 
519     @Test
managedProfileAdded_tileAdded()520     public void managedProfileAdded_tileAdded() {
521         when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(false);
522         when(mAutoAddTracker.getRestoredTilePosition(eq("work"))).thenReturn(2);
523         mAutoTileManager = createAutoTileManager(mContext);
524         Mockito.doAnswer((Answer<Object>) invocation -> {
525             mManagedProfileCallback = invocation.getArgument(0);
526             return null;
527         }).when(mManagedProfileController).addCallback(any());
528         mAutoTileManager.init();
529         when(mManagedProfileController.hasActiveProfile()).thenReturn(true);
530 
531         mManagedProfileCallback.onManagedProfileChanged();
532 
533         verify(mQsHost, times(1)).addTile(eq("work"), eq(2));
534         verify(mAutoAddTracker, times(1)).setTileAdded(eq("work"));
535     }
536 
537     @Test
managedProfileRemoved_tileRemoved()538     public void managedProfileRemoved_tileRemoved() {
539         when(mAutoAddTracker.isAdded(eq("work"))).thenReturn(true);
540         mAutoTileManager = createAutoTileManager(mContext);
541         Mockito.doAnswer((Answer<Object>) invocation -> {
542             mManagedProfileCallback = invocation.getArgument(0);
543             return null;
544         }).when(mManagedProfileController).addCallback(any());
545         mAutoTileManager.init();
546         when(mManagedProfileController.hasActiveProfile()).thenReturn(false);
547 
548         mManagedProfileCallback.onManagedProfileChanged();
549 
550         verify(mQsHost, times(1)).removeTile(eq("work"));
551         verify(mAutoAddTracker, times(1)).setTileRemoved(eq("work"));
552     }
553 
554     @Test
testAddControlsTileIfNotPresent()555     public void testAddControlsTileIfNotPresent() {
556         String spec = DEVICE_CONTROLS;
557         when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
558         when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
559 
560         mAutoTileManager.init();
561         ArgumentCaptor<DeviceControlsController.Callback> captor =
562                 ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
563 
564         verify(mDeviceControlsController).setCallback(captor.capture());
565 
566         captor.getValue().onControlsUpdate(3);
567         verify(mQsHost).addTile(spec, 3);
568         verify(mAutoAddTracker).setTileAdded(spec);
569     }
570 
571     @Test
testDontAddControlsTileIfPresent()572     public void testDontAddControlsTileIfPresent() {
573         String spec = DEVICE_CONTROLS;
574         when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(false);
575         when(mQsHost.getTiles()).thenReturn(new ArrayList<>());
576 
577         mAutoTileManager.init();
578         ArgumentCaptor<DeviceControlsController.Callback> captor =
579                 ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
580 
581         verify(mDeviceControlsController).setCallback(captor.capture());
582 
583         captor.getValue().removeControlsAutoTracker();
584         verify(mQsHost, never()).addTile(spec, 3);
585         verify(mAutoAddTracker, never()).setTileAdded(spec);
586         verify(mAutoAddTracker).setTileRemoved(spec);
587     }
588 
589     @Test
testRemoveControlsTileFromTrackerWhenRequested()590     public void testRemoveControlsTileFromTrackerWhenRequested() {
591         String spec = "controls";
592         when(mAutoAddTracker.isAdded(eq(spec))).thenReturn(true);
593         QSTile mockTile = mock(QSTile.class);
594         when(mockTile.getTileSpec()).thenReturn(spec);
595         when(mQsHost.getTiles()).thenReturn(List.of(mockTile));
596 
597         mAutoTileManager.init();
598         ArgumentCaptor<DeviceControlsController.Callback> captor =
599                 ArgumentCaptor.forClass(DeviceControlsController.Callback.class);
600 
601         verify(mDeviceControlsController).setCallback(captor.capture());
602 
603         captor.getValue().onControlsUpdate(3);
604         verify(mQsHost, never()).addTile(spec, 3);
605         verify(mAutoAddTracker, never()).setTileAdded(spec);
606     }
607 
608 
609     @Test
testEmptyArray_doesNotCrash()610     public void testEmptyArray_doesNotCrash() {
611         mContext.getOrCreateTestableResources().addOverride(
612                 R.array.config_quickSettingsAutoAdd, new String[0]);
613         createAutoTileManager(mContext).destroy();
614     }
615 
616     @Test
testMissingConfig_doesNotCrash()617     public void testMissingConfig_doesNotCrash() {
618         mContext.getOrCreateTestableResources().addOverride(
619                 R.array.config_quickSettingsAutoAdd, null);
620         createAutoTileManager(mContext).destroy();
621     }
622 
623     @Test
testUserChange_newNightDisplayListenerCreated()624     public void testUserChange_newNightDisplayListenerCreated() {
625         UserHandle newUser = UserHandle.of(1000);
626         mAutoTileManager.changeUser(newUser);
627         InOrder inOrder = inOrder(mNightDisplayListenerBuilder);
628         inOrder.verify(mNightDisplayListenerBuilder).setUser(newUser.getIdentifier());
629         inOrder.verify(mNightDisplayListenerBuilder).build();
630     }
631 
632     // Will only notify if it's listening
changeValue(String key, int value)633     private void changeValue(String key, int value) {
634         mSecureSettings.putIntForUser(key, value, USER);
635         TestableLooper.get(this).processAllMessages();
636     }
637 }
638