1 /*
2  * Copyright (C) 2022 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.tiles;
18 
19 import static junit.framework.TestCase.assertEquals;
20 import static junit.framework.TestCase.assertFalse;
21 import static junit.framework.TestCase.assertTrue;
22 
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.Mockito.mock;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.verify;
27 import static org.mockito.Mockito.when;
28 
29 import android.content.BroadcastReceiver;
30 import android.content.ComponentName;
31 import android.content.Intent;
32 import android.content.pm.UserInfo;
33 import android.os.Handler;
34 import android.os.RemoteException;
35 import android.provider.Settings;
36 import android.service.dreams.IDreamManager;
37 import android.service.quicksettings.Tile;
38 import android.testing.TestableLooper;
39 
40 import androidx.test.ext.junit.runners.AndroidJUnit4;
41 import androidx.test.filters.SmallTest;
42 
43 import com.android.internal.logging.MetricsLogger;
44 import com.android.systemui.SysuiTestCase;
45 import com.android.systemui.broadcast.BroadcastDispatcher;
46 import com.android.systemui.classifier.FalsingManagerFake;
47 import com.android.systemui.plugins.ActivityStarter;
48 import com.android.systemui.plugins.statusbar.StatusBarStateController;
49 import com.android.systemui.qs.QSHost;
50 import com.android.systemui.qs.QsEventLogger;
51 import com.android.systemui.qs.logging.QSLogger;
52 import com.android.systemui.qs.tileimpl.QSTileImpl;
53 import com.android.systemui.res.R;
54 import com.android.systemui.settings.UserTracker;
55 import com.android.systemui.util.settings.FakeSettings;
56 import com.android.systemui.util.settings.SecureSettings;
57 
58 import org.junit.After;
59 import org.junit.Before;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.ArgumentCaptor;
63 import org.mockito.Mock;
64 import org.mockito.MockitoAnnotations;
65 
66 @RunWith(AndroidJUnit4.class)
67 @TestableLooper.RunWithLooper(setAsMainLooper = true)
68 @SmallTest
69 public class DreamTileTest extends SysuiTestCase {
70 
71     @Mock
72     private ActivityStarter mActivityStarter;
73     @Mock
74     private QSHost mHost;
75     @Mock
76     private MetricsLogger mMetricsLogger;
77     @Mock
78     private QSLogger mQSLogger;
79     @Mock
80     private StatusBarStateController mStatusBarStateController;
81     @Mock
82     private IDreamManager mDreamManager;
83     @Mock
84     private BroadcastDispatcher mBroadcastDispatcher;
85     @Mock
86     private UserTracker mUserTracker;
87     @Mock
88     private QsEventLogger mUiEventLogger;
89 
90     private TestableLooper mTestableLooper;
91 
92     private DreamTile mTile;
93 
94     private SecureSettings mSecureSettings;
95 
96     private static final ComponentName COLORS_DREAM_COMPONENT_NAME = new ComponentName(
97             "com.android.dreams", ".Colors");
98 
99     private static final int DEFAULT_USER = 0;
100 
101     private final String mExpectedTileLabel = mContext.getResources().getString(
102             R.string.quick_settings_screensaver_label);
103 
104     @Before
setUp()105     public void setUp() throws Exception {
106         MockitoAnnotations.initMocks(this);
107         mSecureSettings = new FakeSettings();
108         mTestableLooper = TestableLooper.get(this);
109 
110         when(mHost.getUserId()).thenReturn(DEFAULT_USER);
111         when(mHost.getContext()).thenReturn(mContext);
112 
113         mTile = spy(constructTileForTest(true, false));
114 
115         mTestableLooper.processAllMessages();
116         mTile.initialize();
117     }
118 
119     @After
tearDown()120     public void tearDown() {
121         mTile.destroy();
122         mTestableLooper.processAllMessages();
123     }
124 
125     @Test
testNotAvailable()126     public void testNotAvailable() throws RemoteException {
127         // Should not be available if screensaver is disabled
128         setScreensaverEnabled(false);
129 
130         mTile.refreshState();
131         mTestableLooper.processAllMessages();
132         assertEquals(Tile.STATE_UNAVAILABLE, mTile.getState().state);
133 
134         // Should not be available if component is not set
135         mSecureSettings.putInt(Settings.Secure.SCREENSAVER_ENABLED, 1);
136         when(mDreamManager.getDreamComponentsForUser(mUserTracker.getUserId())).thenReturn(null);
137 
138         mTestableLooper.processAllMessages();
139         assertEquals(Tile.STATE_UNAVAILABLE, mTile.getState().state);
140         assertEquals(mExpectedTileLabel, mTile.getState().contentDescription);
141     }
142 
143     @Test
testInactiveWhenDreaming()144     public void testInactiveWhenDreaming() throws RemoteException {
145         setScreensaverEnabled(true);
146 
147         when(mDreamManager.getDreamComponentsForUser(mUserTracker.getUserId()))
148              .thenReturn(new ComponentName[]{COLORS_DREAM_COMPONENT_NAME});
149         when(mDreamManager.isDreaming()).thenReturn(false);
150 
151         mTile.refreshState();
152         mTestableLooper.processAllMessages();
153         assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
154     }
155 
156     @Test
testActive()157     public void testActive() throws RemoteException {
158         setScreensaverEnabled(true);
159 
160         when(mDreamManager.getDreamComponentsForUser(mUserTracker.getUserId()))
161              .thenReturn(new ComponentName[]{COLORS_DREAM_COMPONENT_NAME});
162         when(mDreamManager.isDreaming()).thenReturn(true);
163 
164         mTile.refreshState();
165         mTestableLooper.processAllMessages();
166         assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
167     }
168 
169     @Test
testClick()170     public void testClick() throws RemoteException {
171         // Set the AOSP dream enabled as the base setup.
172         setScreensaverEnabled(true);
173         when(mDreamManager.getDreamComponentsForUser(mUserTracker.getUserId()))
174              .thenReturn(new ComponentName[]{COLORS_DREAM_COMPONENT_NAME});
175         when(mDreamManager.isDreaming()).thenReturn(false);
176 
177         mTile.refreshState();
178         mTestableLooper.processAllMessages();
179         assertEquals(Tile.STATE_INACTIVE, mTile.getState().state);
180 
181         // Now click
182         mTile.handleClick(null /* view */);
183 
184         verify(mDreamManager).dream();
185 
186         when(mDreamManager.isDreaming()).thenReturn(true);
187         mTile.refreshState();
188         mTestableLooper.processAllMessages();
189         assertEquals(Tile.STATE_ACTIVE, mTile.getState().state);
190 
191         // Click again to see that other method is called
192         mTile.handleClick(null /* view */);
193 
194         verify(mDreamManager).awaken();
195     }
196 
197     @Test
testContentDescription()198     public void testContentDescription() {
199         assertEquals(mExpectedTileLabel, mTile.getContentDescription(null));
200 
201         final String testDreamName = "MyDream";
202         assertEquals(mExpectedTileLabel + ", " + testDreamName,
203                 mTile.getContentDescription(testDreamName));
204     }
205 
206     @Test
testUserAvailability()207     public void testUserAvailability() {
208         DreamTile unsupportedTile = constructTileForTest(false, true);
209         assertFalse(unsupportedTile.isAvailable());
210 
211         DreamTile supportedTileAllUsers = constructTileForTest(true, false);
212 
213         UserInfo mainUserInfo = mock(UserInfo.class);
214         when(mainUserInfo.isMain()).thenReturn(true);
215 
216         UserInfo nonMainUserInfo = mock(UserInfo.class);
217         when(nonMainUserInfo.isMain()).thenReturn(false);
218 
219         when(mUserTracker.getUserInfo()).thenReturn(mainUserInfo);
220         assertTrue(supportedTileAllUsers.isAvailable());
221         when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo);
222         assertTrue(supportedTileAllUsers.isAvailable());
223 
224         DreamTile supportedTileOnlySystemUser = constructTileForTest(true, true);
225         when(mUserTracker.getUserInfo()).thenReturn(mainUserInfo);
226         assertTrue(supportedTileOnlySystemUser.isAvailable());
227         when(mUserTracker.getUserInfo()).thenReturn(nonMainUserInfo);
228         assertFalse(supportedTileOnlySystemUser.isAvailable());
229 
230         destroyTile(unsupportedTile);
231         destroyTile(supportedTileAllUsers);
232         destroyTile(supportedTileOnlySystemUser);
233     }
234 
235     @Test
testIconDockState()236     public void testIconDockState() {
237         final DreamTile dockedTile = constructTileForTest(true, false);
238 
239         final ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(
240                 BroadcastReceiver.class);
241         dockedTile.handleSetListening(true);
242         verify(mBroadcastDispatcher).registerReceiver(receiverCaptor.capture(), any());
243         final BroadcastReceiver receiver = receiverCaptor.getValue();
244 
245         Intent dockIntent = new Intent(Intent.ACTION_DOCK_EVENT);
246         dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_DESK);
247         receiver.onReceive(mContext, dockIntent);
248         mTestableLooper.processAllMessages();
249         assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver),
250                 dockedTile.getState().icon);
251 
252         dockIntent.putExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
253         receiver.onReceive(mContext, dockIntent);
254         mTestableLooper.processAllMessages();
255         assertEquals(QSTileImpl.ResourceIcon.get(R.drawable.ic_qs_screen_saver_undocked),
256                 dockedTile.getState().icon);
257 
258         destroyTile(dockedTile);
259     }
260 
setScreensaverEnabled(boolean enabled)261     private void setScreensaverEnabled(boolean enabled) {
262         mSecureSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, enabled ? 1 : 0,
263                 DEFAULT_USER);
264     }
265 
destroyTile(QSTileImpl<?> tile)266     private void destroyTile(QSTileImpl<?> tile) {
267         tile.destroy();
268         mTestableLooper.processAllMessages();
269     }
270 
constructTileForTest(boolean dreamSupported, boolean dreamOnlyEnabledForSystemUser)271     private DreamTile constructTileForTest(boolean dreamSupported,
272             boolean dreamOnlyEnabledForSystemUser) {
273         return new DreamTile(
274                 mHost,
275                 mUiEventLogger,
276                 mTestableLooper.getLooper(),
277                 new Handler(mTestableLooper.getLooper()),
278                 new FalsingManagerFake(),
279                 mMetricsLogger,
280                 mStatusBarStateController,
281                 mActivityStarter,
282                 mQSLogger,
283                 mDreamManager,
284                 mSecureSettings,
285                 mBroadcastDispatcher,
286                 mUserTracker,
287                 dreamSupported, dreamOnlyEnabledForSystemUser);
288     }
289 }
290