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.qs;
18 
19 
20 import static junit.framework.Assert.assertEquals;
21 import static junit.framework.Assert.assertFalse;
22 import static junit.framework.Assert.assertTrue;
23 
24 import static org.mockito.ArgumentMatchers.any;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.anyString;
27 import static org.mockito.ArgumentMatchers.isNull;
28 import static org.mockito.Mockito.clearInvocations;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.verify;
32 import static org.mockito.Mockito.when;
33 
34 import android.content.ComponentName;
35 import android.content.Context;
36 import android.content.Intent;
37 import android.content.SharedPreferences;
38 import android.database.ContentObserver;
39 import android.os.Handler;
40 import android.os.Looper;
41 import android.os.UserHandle;
42 import android.testing.AndroidTestingRunner;
43 import android.util.SparseArray;
44 import android.view.View;
45 
46 import androidx.annotation.Nullable;
47 import androidx.test.filters.SmallTest;
48 
49 import com.android.internal.logging.MetricsLogger;
50 import com.android.internal.util.CollectionUtils;
51 import com.android.systemui.R;
52 import com.android.systemui.SysuiTestCase;
53 import com.android.systemui.classifier.FalsingManagerFake;
54 import com.android.systemui.dump.nano.SystemUIProtoDump;
55 import com.android.systemui.flags.FakeFeatureFlags;
56 import com.android.systemui.flags.FeatureFlags;
57 import com.android.systemui.flags.Flags;
58 import com.android.systemui.plugins.ActivityStarter;
59 import com.android.systemui.plugins.PluginManager;
60 import com.android.systemui.plugins.qs.QSFactory;
61 import com.android.systemui.plugins.qs.QSTile;
62 import com.android.systemui.plugins.statusbar.StatusBarStateController;
63 import com.android.systemui.qs.external.CustomTile;
64 import com.android.systemui.qs.external.CustomTileStatePersister;
65 import com.android.systemui.qs.external.TileLifecycleManager;
66 import com.android.systemui.qs.external.TileServiceKey;
67 import com.android.systemui.qs.logging.QSLogger;
68 import com.android.systemui.qs.tileimpl.QSTileImpl;
69 import com.android.systemui.settings.UserFileManager;
70 import com.android.systemui.settings.UserTracker;
71 import com.android.systemui.shade.ShadeController;
72 import com.android.systemui.statusbar.phone.AutoTileManager;
73 import com.android.systemui.tuner.TunerService;
74 import com.android.systemui.util.FakeSharedPreferences;
75 import com.android.systemui.util.concurrency.FakeExecutor;
76 import com.android.systemui.util.settings.FakeSettings;
77 import com.android.systemui.util.settings.SecureSettings;
78 import com.android.systemui.util.time.FakeSystemClock;
79 
80 import org.junit.Before;
81 import org.junit.Test;
82 import org.junit.runner.RunWith;
83 import org.mockito.Mock;
84 import org.mockito.MockitoAnnotations;
85 import org.mockito.stubbing.Answer;
86 
87 import java.io.PrintWriter;
88 import java.io.StringWriter;
89 import java.util.List;
90 import java.util.concurrent.Executor;
91 
92 import javax.inject.Provider;
93 
94 @RunWith(AndroidTestingRunner.class)
95 @SmallTest
96 public class QSTileHostTest extends SysuiTestCase {
97 
98     private static String MOCK_STATE_STRING = "MockState";
99     private static ComponentName CUSTOM_TILE =
100             ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
101     private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);
102     private static final String SETTING = QSHost.TILES_SETTING;
103 
104     @Mock
105     private QSFactory mDefaultFactory;
106     @Mock
107     private PluginManager mPluginManager;
108     @Mock
109     private TunerService mTunerService;
110     @Mock
111     private AutoTileManager mAutoTiles;
112     @Mock
113     private ShadeController mShadeController;
114     @Mock
115     private QSLogger mQSLogger;
116     @Mock
117     private CustomTile mCustomTile;
118     @Mock
119     private UserTracker mUserTracker;
120     private SecureSettings mSecureSettings;
121     @Mock
122     private CustomTileStatePersister mCustomTileStatePersister;
123     @Mock
124     private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
125     @Mock
126     private TileLifecycleManager mTileLifecycleManager;
127     @Mock
128     private UserFileManager mUserFileManager;
129 
130     private SparseArray<SharedPreferences> mSharedPreferencesByUser;
131 
132     private FakeFeatureFlags mFeatureFlags;
133 
134     private FakeExecutor mMainExecutor;
135 
136     private QSTileHost mQSTileHost;
137 
138     @Before
setUp()139     public void setUp() {
140         MockitoAnnotations.initMocks(this);
141         mFeatureFlags = new FakeFeatureFlags();
142 
143         mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, false);
144         mFeatureFlags.set(Flags.QS_PIPELINE_AUTO_ADD, false);
145 
146         mMainExecutor = new FakeExecutor(new FakeSystemClock());
147 
148         mSharedPreferencesByUser = new SparseArray<>();
149         when(mTileLifecycleManagerFactory
150                 .create(any(Intent.class), any(UserHandle.class)))
151                 .thenReturn(mTileLifecycleManager);
152         when(mUserFileManager.getSharedPreferences(anyString(), anyInt(), anyInt()))
153                 .thenAnswer((Answer<SharedPreferences>) invocation -> {
154                     assertEquals(QSTileHost.TILES, invocation.getArgument(0));
155                     int userId = invocation.getArgument(2);
156                     if (!mSharedPreferencesByUser.contains(userId)) {
157                         mSharedPreferencesByUser.put(userId, new FakeSharedPreferences());
158                     }
159                     return mSharedPreferencesByUser.get(userId);
160                 });
161 
162         mSecureSettings = new FakeSettings();
163         saveSetting("");
164         mQSTileHost = new TestQSTileHost(mContext, mDefaultFactory, mMainExecutor,
165                 mPluginManager, mTunerService, () -> mAutoTiles, mShadeController,
166                 mQSLogger, mUserTracker, mSecureSettings, mCustomTileStatePersister,
167                 mTileLifecycleManagerFactory, mUserFileManager, mFeatureFlags);
168         mMainExecutor.runAllReady();
169 
170         mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
171             @Override
172             public void onChange(boolean selfChange) {
173                 super.onChange(selfChange);
174                 mMainExecutor.execute(() -> mQSTileHost.onTuningChanged(SETTING, getSetting()));
175                 mMainExecutor.runAllReady();
176             }
177         }, mUserTracker.getUserId());
178         setUpTileFactory();
179     }
180 
saveSetting(String value)181     private void saveSetting(String value) {
182         mSecureSettings.putStringForUser(
183                 SETTING, value, "", false, mUserTracker.getUserId(), false);
184     }
185 
getSetting()186     private String getSetting() {
187         return mSecureSettings.getStringForUser(SETTING, mUserTracker.getUserId());
188     }
189 
setUpTileFactory()190     private void setUpTileFactory() {
191         // Only create this kind of tiles
192         when(mDefaultFactory.createTile(anyString())).thenAnswer(
193                 invocation -> {
194                     String spec = invocation.getArgument(0);
195                     if ("spec1".equals(spec)) {
196                         return new TestTile1(mQSTileHost);
197                     } else if ("spec2".equals(spec)) {
198                         return new TestTile2(mQSTileHost);
199                     } else if ("spec3".equals(spec)) {
200                         return new TestTile3(mQSTileHost);
201                     } else if ("na".equals(spec)) {
202                         return new NotAvailableTile(mQSTileHost);
203                     } else if (CUSTOM_TILE_SPEC.equals(spec)) {
204                         QSTile tile = mCustomTile;
205                         QSTile.State s = mock(QSTile.State.class);
206                         s.spec = spec;
207                         when(mCustomTile.getState()).thenReturn(s);
208                         return tile;
209                     } else if ("internet".equals(spec)
210                             || "wifi".equals(spec)
211                             || "cell".equals(spec)) {
212                         return new TestTile1(mQSTileHost);
213                     } else {
214                         return null;
215                     }
216                 });
217         when(mCustomTile.isAvailable()).thenReturn(true);
218     }
219 
220     @Test
testLoadTileSpecs_emptySetting()221     public void testLoadTileSpecs_emptySetting() {
222         List<String> tiles = QSTileHost.loadTileSpecs(mContext, "");
223         assertFalse(tiles.isEmpty());
224     }
225 
226     @Test
testLoadTileSpecs_nullSetting()227     public void testLoadTileSpecs_nullSetting() {
228         List<String> tiles = QSTileHost.loadTileSpecs(mContext, null);
229         assertFalse(tiles.isEmpty());
230     }
231 
232     @Test
testInvalidSpecUsesDefault()233     public void testInvalidSpecUsesDefault() {
234         mContext.getOrCreateTestableResources()
235                 .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
236         saveSetting("not-valid");
237 
238         assertEquals(2, mQSTileHost.getTiles().size());
239     }
240 
241     @Test
testRemoveWifiAndCellularWithoutInternet()242     public void testRemoveWifiAndCellularWithoutInternet() {
243         saveSetting("wifi, spec1, cell, spec2");
244 
245         assertEquals("internet", mQSTileHost.getSpecs().get(0));
246         assertEquals("spec1", mQSTileHost.getSpecs().get(1));
247         assertEquals("spec2", mQSTileHost.getSpecs().get(2));
248     }
249 
250     @Test
testRemoveWifiAndCellularWithInternet()251     public void testRemoveWifiAndCellularWithInternet() {
252         saveSetting("wifi, spec1, cell, spec2, internet");
253 
254         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
255         assertEquals("spec2", mQSTileHost.getSpecs().get(1));
256         assertEquals("internet", mQSTileHost.getSpecs().get(2));
257     }
258 
259     @Test
testRemoveWifiWithoutInternet()260     public void testRemoveWifiWithoutInternet() {
261         saveSetting("spec1, wifi, spec2");
262 
263         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
264         assertEquals("internet", mQSTileHost.getSpecs().get(1));
265         assertEquals("spec2", mQSTileHost.getSpecs().get(2));
266     }
267 
268     @Test
testRemoveCellWithInternet()269     public void testRemoveCellWithInternet() {
270         saveSetting("spec1, spec2, cell, internet");
271 
272         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
273         assertEquals("spec2", mQSTileHost.getSpecs().get(1));
274         assertEquals("internet", mQSTileHost.getSpecs().get(2));
275     }
276 
277     @Test
testNoWifiNoCellularNoInternet()278     public void testNoWifiNoCellularNoInternet() {
279         saveSetting("spec1,spec2");
280 
281         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
282         assertEquals("spec2", mQSTileHost.getSpecs().get(1));
283     }
284 
285     @Test
testSpecWithInvalidDoesNotUseDefault()286     public void testSpecWithInvalidDoesNotUseDefault() {
287         mContext.getOrCreateTestableResources()
288                 .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
289         saveSetting("spec2,not-valid");
290 
291         assertEquals(1, mQSTileHost.getTiles().size());
292         QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
293         assertTrue(element instanceof TestTile2);
294     }
295 
296     @Test
testDump()297     public void testDump() {
298         saveSetting("spec1,spec2");
299         StringWriter w = new StringWriter();
300         PrintWriter pw = new PrintWriter(w);
301         mQSTileHost.dump(pw, new String[]{});
302 
303         String output = "QSTileHost:" + "\n"
304                 + "tile specs: [spec1, spec2]" + "\n"
305                 + "current user: 0" + "\n"
306                 + "is dirty: false" + "\n"
307                 + "tiles:" + "\n"
308                 + "TestTile1:" + "\n"
309                 + "    MockState" + "\n"
310                 + "TestTile2:" + "\n"
311                 + "    MockState" + "\n";
312 
313         System.out.println(output);
314         System.out.println(w.getBuffer().toString());
315 
316         assertEquals(output, w.getBuffer().toString());
317     }
318 
319     @Test
testDefault()320     public void testDefault() {
321         mContext.getOrCreateTestableResources()
322                 .addOverride(R.string.quick_settings_tiles_default, "spec1");
323         saveSetting("default");
324         assertEquals(1, mQSTileHost.getTiles().size());
325         QSTile element = CollectionUtils.firstOrNull(mQSTileHost.getTiles());
326         assertTrue(element instanceof TestTile1);
327         verify(mQSLogger).logTileAdded("spec1");
328     }
329 
330     @Test
testNoRepeatedSpecs_addTile()331     public void testNoRepeatedSpecs_addTile() {
332         mContext.getOrCreateTestableResources()
333                 .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
334         saveSetting("spec1,spec2");
335 
336         mQSTileHost.addTile("spec1");
337 
338         assertEquals(2, mQSTileHost.getSpecs().size());
339         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
340         assertEquals("spec2", mQSTileHost.getSpecs().get(1));
341     }
342 
343     @Test
testAddTileAtValidPosition()344     public void testAddTileAtValidPosition() {
345         mContext.getOrCreateTestableResources()
346                 .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
347         saveSetting("spec1,spec3");
348 
349         mQSTileHost.addTile("spec2", 1);
350         mMainExecutor.runAllReady();
351 
352         assertEquals(3, mQSTileHost.getSpecs().size());
353         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
354         assertEquals("spec2", mQSTileHost.getSpecs().get(1));
355         assertEquals("spec3", mQSTileHost.getSpecs().get(2));
356     }
357 
358     @Test
testAddTileAtInvalidPositionAddsToEnd()359     public void testAddTileAtInvalidPositionAddsToEnd() {
360         mContext.getOrCreateTestableResources()
361                 .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
362         saveSetting("spec1,spec3");
363 
364         mQSTileHost.addTile("spec2", 100);
365         mMainExecutor.runAllReady();
366 
367         assertEquals(3, mQSTileHost.getSpecs().size());
368         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
369         assertEquals("spec3", mQSTileHost.getSpecs().get(1));
370         assertEquals("spec2", mQSTileHost.getSpecs().get(2));
371     }
372 
373     @Test
testAddTileAtEnd()374     public void testAddTileAtEnd() {
375         mContext.getOrCreateTestableResources()
376                 .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
377         saveSetting("spec1,spec3");
378 
379         mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
380         mMainExecutor.runAllReady();
381 
382         assertEquals(3, mQSTileHost.getSpecs().size());
383         assertEquals("spec1", mQSTileHost.getSpecs().get(0));
384         assertEquals("spec3", mQSTileHost.getSpecs().get(1));
385         assertEquals("spec2", mQSTileHost.getSpecs().get(2));
386     }
387 
388     @Test
testNoRepeatedSpecs_customTile()389     public void testNoRepeatedSpecs_customTile() {
390         saveSetting(CUSTOM_TILE_SPEC);
391 
392         mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
393         mMainExecutor.runAllReady();
394 
395         assertEquals(1, mQSTileHost.getSpecs().size());
396         assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
397     }
398 
399     @Test
testAddedAtBeginningOnDefault_customTile()400     public void testAddedAtBeginningOnDefault_customTile() {
401         saveSetting("spec1"); // seed
402 
403         mQSTileHost.addTile(CUSTOM_TILE);
404         mMainExecutor.runAllReady();
405 
406         assertEquals(2, mQSTileHost.getSpecs().size());
407         assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
408     }
409 
410     @Test
testAddedAtBeginning_customTile()411     public void testAddedAtBeginning_customTile() {
412         saveSetting("spec1"); // seed
413 
414         mQSTileHost.addTile(CUSTOM_TILE, /* end */ false);
415         mMainExecutor.runAllReady();
416 
417         assertEquals(2, mQSTileHost.getSpecs().size());
418         assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(0));
419     }
420 
421     @Test
testAddedAtEnd_customTile()422     public void testAddedAtEnd_customTile() {
423         saveSetting("spec1"); // seed
424 
425         mQSTileHost.addTile(CUSTOM_TILE, /* end */ true);
426         mMainExecutor.runAllReady();
427 
428         assertEquals(2, mQSTileHost.getSpecs().size());
429         assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.getSpecs().get(1));
430     }
431 
432     @Test
testLoadTileSpec_repeated()433     public void testLoadTileSpec_repeated() {
434         List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");
435 
436         assertEquals(2, specs.size());
437         assertEquals("spec1", specs.get(0));
438         assertEquals("spec2", specs.get(1));
439     }
440 
441     @Test
testLoadTileSpec_repeatedInDefault()442     public void testLoadTileSpec_repeatedInDefault() {
443         mContext.getOrCreateTestableResources()
444                 .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
445         List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");
446 
447         // Remove spurious tiles, like dbg:mem
448         specs.removeIf(spec -> !"spec1".equals(spec));
449         assertEquals(1, specs.size());
450     }
451 
452     @Test
testLoadTileSpec_repeatedDefaultAndSetting()453     public void testLoadTileSpec_repeatedDefaultAndSetting() {
454         mContext.getOrCreateTestableResources()
455                 .addOverride(R.string.quick_settings_tiles_default, "spec1");
456         List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");
457 
458         // Remove spurious tiles, like dbg:mem
459         specs.removeIf(spec -> !"spec1".equals(spec));
460         assertEquals(1, specs.size());
461     }
462 
463     @Test
testNotAvailableTile_specNotNull()464     public void testNotAvailableTile_specNotNull() {
465         saveSetting("na");
466         verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
467     }
468 
469     @Test
testCustomTileRemoved_stateDeleted()470     public void testCustomTileRemoved_stateDeleted() {
471         mQSTileHost.changeTilesByUser(List.of(CUSTOM_TILE_SPEC), List.of());
472 
473         verify(mCustomTileStatePersister)
474                 .removeState(new TileServiceKey(CUSTOM_TILE, mQSTileHost.getUserId()));
475     }
476 
477     @Test
testRemoveTiles()478     public void testRemoveTiles() {
479         saveSetting("spec1,spec2,spec3");
480 
481         mQSTileHost.removeTiles(List.of("spec1", "spec2"));
482 
483         mMainExecutor.runAllReady();
484         assertEquals(List.of("spec3"), mQSTileHost.getSpecs());
485     }
486 
487     @Test
testTilesRemovedInQuickSuccession()488     public void testTilesRemovedInQuickSuccession() {
489         saveSetting("spec1,spec2,spec3");
490         mQSTileHost.removeTile("spec1");
491         mQSTileHost.removeTile("spec3");
492 
493         mMainExecutor.runAllReady();
494         assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
495         assertEquals("spec2", getSetting());
496     }
497 
498     @Test
testAddTileInMainThread()499     public void testAddTileInMainThread() {
500         saveSetting("spec1,spec2");
501 
502         mQSTileHost.addTile("spec3");
503         assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
504 
505         mMainExecutor.runAllReady();
506         assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
507     }
508 
509     @Test
testRemoveTileInMainThread()510     public void testRemoveTileInMainThread() {
511         saveSetting("spec1,spec2");
512 
513         mQSTileHost.removeTile("spec1");
514         assertEquals(List.of("spec1", "spec2"), mQSTileHost.getSpecs());
515 
516         mMainExecutor.runAllReady();
517         assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
518     }
519 
520     @Test
testRemoveTilesInMainThread()521     public void testRemoveTilesInMainThread() {
522         saveSetting("spec1,spec2,spec3");
523 
524         mQSTileHost.removeTiles(List.of("spec3", "spec1"));
525         assertEquals(List.of("spec1", "spec2", "spec3"), mQSTileHost.getSpecs());
526 
527         mMainExecutor.runAllReady();
528         assertEquals(List.of("spec2"), mQSTileHost.getSpecs());
529     }
530 
531     @Test
testRemoveTileByUserInMainThread()532     public void testRemoveTileByUserInMainThread() {
533         saveSetting("spec1," + CUSTOM_TILE_SPEC);
534 
535         mQSTileHost.removeTileByUser(CUSTOM_TILE);
536         assertEquals(List.of("spec1", CUSTOM_TILE_SPEC), mQSTileHost.getSpecs());
537 
538         mMainExecutor.runAllReady();
539         assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
540     }
541 
542     @Test
testNonValidTileNotStoredInSettings()543     public void testNonValidTileNotStoredInSettings() {
544         saveSetting("spec1,not-valid");
545 
546         assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
547         assertEquals("spec1", getSetting());
548     }
549 
550     @Test
testNotAvailableTileNotStoredInSettings()551     public void testNotAvailableTileNotStoredInSettings() {
552         saveSetting("spec1,na");
553 
554         assertEquals(List.of("spec1"), mQSTileHost.getSpecs());
555         assertEquals("spec1", getSetting());
556     }
557 
558     @Test
testIsTileAdded_true()559     public void testIsTileAdded_true() {
560         int user = mUserTracker.getUserId();
561         getSharedPreferencesForUser(user)
562                 .edit()
563                 .putBoolean(CUSTOM_TILE.flattenToString(), true)
564                 .apply();
565 
566         assertTrue(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
567     }
568 
569     @Test
testIsTileAdded_false()570     public void testIsTileAdded_false() {
571         int user = mUserTracker.getUserId();
572         getSharedPreferencesForUser(user)
573                 .edit()
574                 .putBoolean(CUSTOM_TILE.flattenToString(), false)
575                 .apply();
576 
577         assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
578     }
579 
580     @Test
testIsTileAdded_notSet()581     public void testIsTileAdded_notSet() {
582         int user = mUserTracker.getUserId();
583 
584         assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user));
585     }
586 
587     @Test
testIsTileAdded_differentUser()588     public void testIsTileAdded_differentUser() {
589         int user = mUserTracker.getUserId();
590         mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user)
591                 .edit()
592                 .putBoolean(CUSTOM_TILE.flattenToString(), true)
593                 .apply();
594 
595         assertFalse(mQSTileHost.isTileAdded(CUSTOM_TILE, user + 1));
596     }
597 
598     @Test
testSetTileAdded_true()599     public void testSetTileAdded_true() {
600         int user = mUserTracker.getUserId();
601         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
602 
603         assertTrue(getSharedPreferencesForUser(user)
604                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
605     }
606 
607     @Test
testSetTileAdded_false()608     public void testSetTileAdded_false() {
609         int user = mUserTracker.getUserId();
610         mQSTileHost.setTileAdded(CUSTOM_TILE, user, false);
611 
612         assertFalse(getSharedPreferencesForUser(user)
613                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
614     }
615 
616     @Test
testSetTileAdded_differentUser()617     public void testSetTileAdded_differentUser() {
618         int user = mUserTracker.getUserId();
619         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
620 
621         assertFalse(getSharedPreferencesForUser(user + 1)
622                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
623     }
624 
625     @Test
testSetTileRemoved_afterCustomTileChangedByUser()626     public void testSetTileRemoved_afterCustomTileChangedByUser() {
627         int user = mUserTracker.getUserId();
628         saveSetting(CUSTOM_TILE_SPEC);
629 
630         // This will be done by TileServiceManager
631         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
632 
633         mQSTileHost.changeTilesByUser(mQSTileHost.getSpecs(), List.of("spec1"));
634         assertFalse(getSharedPreferencesForUser(user)
635                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
636     }
637 
638     @Test
testSetTileRemoved_removedByUser()639     public void testSetTileRemoved_removedByUser() {
640         int user = mUserTracker.getUserId();
641         saveSetting(CUSTOM_TILE_SPEC);
642 
643         // This will be done by TileServiceManager
644         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
645 
646         mQSTileHost.removeTileByUser(CUSTOM_TILE);
647         mMainExecutor.runAllReady();
648         assertFalse(getSharedPreferencesForUser(user)
649                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
650     }
651 
652     @Test
testSetTileRemoved_removedBySystem()653     public void testSetTileRemoved_removedBySystem() {
654         int user = mUserTracker.getUserId();
655         saveSetting("spec1," + CUSTOM_TILE_SPEC);
656 
657         // This will be done by TileServiceManager
658         mQSTileHost.setTileAdded(CUSTOM_TILE, user, true);
659 
660         mQSTileHost.removeTile(CUSTOM_TILE_SPEC);
661         mMainExecutor.runAllReady();
662         assertFalse(getSharedPreferencesForUser(user)
663                 .getBoolean(CUSTOM_TILE.flattenToString(), false));
664     }
665 
666     @Test
testProtoDump_noTiles()667     public void testProtoDump_noTiles() {
668         SystemUIProtoDump proto = new SystemUIProtoDump();
669         mQSTileHost.dumpProto(proto, new String[0]);
670 
671         assertEquals(0, proto.tiles.length);
672     }
673 
674     @Test
testTilesInOrder()675     public void testTilesInOrder() {
676         saveSetting("spec1," + CUSTOM_TILE_SPEC);
677 
678         SystemUIProtoDump proto = new SystemUIProtoDump();
679         mQSTileHost.dumpProto(proto, new String[0]);
680 
681         assertEquals(2, proto.tiles.length);
682         assertEquals("spec1", proto.tiles[0].getSpec());
683         assertEquals(CUSTOM_TILE.getPackageName(), proto.tiles[1].getComponentName().packageName);
684         assertEquals(CUSTOM_TILE.getClassName(), proto.tiles[1].getComponentName().className);
685     }
686 
687     @Test
testUserChange_flagOn_autoTileManagerNotified()688     public void testUserChange_flagOn_autoTileManagerNotified() {
689         mFeatureFlags.set(Flags.QS_PIPELINE_NEW_HOST, true);
690         int currentUser = mUserTracker.getUserId();
691         clearInvocations(mAutoTiles);
692         when(mUserTracker.getUserId()).thenReturn(currentUser + 1);
693 
694         mQSTileHost.onTuningChanged(SETTING, "a,b");
695         verify(mAutoTiles).changeUser(UserHandle.of(currentUser + 1));
696     }
697 
getSharedPreferencesForUser(int user)698     private SharedPreferences getSharedPreferencesForUser(int user) {
699         return mUserFileManager.getSharedPreferences(QSTileHost.TILES, 0, user);
700     }
701 
702     private class TestQSTileHost extends QSTileHost {
TestQSTileHost(Context context, QSFactory defaultFactory, Executor mainExecutor, PluginManager pluginManager, TunerService tunerService, Provider<AutoTileManager> autoTiles, ShadeController shadeController, QSLogger qsLogger, UserTracker userTracker, SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister, TileLifecycleManager.Factory tileLifecycleManagerFactory, UserFileManager userFileManager, FeatureFlags featureFlags)703         TestQSTileHost(Context context,
704                 QSFactory defaultFactory, Executor mainExecutor,
705                 PluginManager pluginManager, TunerService tunerService,
706                 Provider<AutoTileManager> autoTiles,
707                 ShadeController shadeController, QSLogger qsLogger,
708                 UserTracker userTracker, SecureSettings secureSettings,
709                 CustomTileStatePersister customTileStatePersister,
710                 TileLifecycleManager.Factory tileLifecycleManagerFactory,
711                 UserFileManager userFileManager, FeatureFlags featureFlags) {
712             super(context, defaultFactory, mainExecutor, pluginManager,
713                     tunerService, autoTiles,  shadeController, qsLogger,
714                     userTracker, secureSettings, customTileStatePersister,
715                     tileLifecycleManagerFactory, userFileManager, featureFlags);
716         }
717 
718         @Override
onPluginConnected(QSFactory plugin, Context pluginContext)719         public void onPluginConnected(QSFactory plugin, Context pluginContext) {
720         }
721 
722         @Override
onPluginDisconnected(QSFactory plugin)723         public void onPluginDisconnected(QSFactory plugin) {
724         }
725     }
726 
727 
728     private class TestTile extends QSTileImpl<QSTile.State> {
729 
TestTile(QSHost host)730         protected TestTile(QSHost host) {
731             super(
732                     host,
733                     mock(QsEventLogger.class),
734                     mock(Looper.class),
735                     mock(Handler.class),
736                     new FalsingManagerFake(),
737                     mock(MetricsLogger.class),
738                     mock(StatusBarStateController.class),
739                     mock(ActivityStarter.class),
740                     QSTileHostTest.this.mQSLogger
741             );
742         }
743 
744         @Override
newTileState()745         public State newTileState() {
746             State s = mock(QSTile.State.class);
747             when(s.toString()).thenReturn(MOCK_STATE_STRING);
748             return s;
749         }
750 
751         @Override
handleClick(@ullable View view)752         protected void handleClick(@Nullable View view) {}
753 
754         @Override
handleUpdateState(State state, Object arg)755         protected void handleUpdateState(State state, Object arg) {}
756 
757         @Override
getMetricsCategory()758         public int getMetricsCategory() {
759             return 0;
760         }
761 
762         @Override
getLongClickIntent()763         public Intent getLongClickIntent() {
764             return null;
765         }
766 
767         @Override
getTileLabel()768         public CharSequence getTileLabel() {
769             return null;
770         }
771     }
772 
773     private class TestTile1 extends TestTile {
774 
TestTile1(QSHost host)775         protected TestTile1(QSHost host) {
776             super(host);
777         }
778     }
779 
780     private class TestTile2 extends TestTile {
781 
TestTile2(QSHost host)782         protected TestTile2(QSHost host) {
783             super(host);
784         }
785     }
786 
787     private class TestTile3 extends TestTile {
788 
TestTile3(QSHost host)789         protected TestTile3(QSHost host) {
790             super(host);
791         }
792     }
793 
794     private class NotAvailableTile extends TestTile {
795 
NotAvailableTile(QSHost host)796         protected NotAvailableTile(QSHost host) {
797             super(host);
798         }
799 
800         @Override
isAvailable()801         public boolean isAvailable() {
802             return false;
803         }
804     }
805 }
806