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.complication;
18 
19 import static com.android.systemui.complication.Complication.COMPLICATION_TYPE_HOME_CONTROLS;
20 import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static org.mockito.ArgumentMatchers.anyInt;
25 import static org.mockito.Mockito.mock;
26 import static org.mockito.Mockito.never;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.when;
29 
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.res.Resources;
33 import android.testing.AndroidTestingRunner;
34 import android.view.View;
35 
36 import androidx.test.filters.SmallTest;
37 
38 import com.android.internal.logging.UiEventLogger;
39 import com.android.systemui.SysuiTestCase;
40 import com.android.systemui.animation.view.LaunchableImageView;
41 import com.android.systemui.complication.dagger.DreamHomeControlsComplicationComponent;
42 import com.android.systemui.condition.SelfExecutingMonitor;
43 import com.android.systemui.controls.ControlsServiceInfo;
44 import com.android.systemui.controls.controller.ControlsController;
45 import com.android.systemui.controls.controller.StructureInfo;
46 import com.android.systemui.controls.dagger.ControlsComponent;
47 import com.android.systemui.controls.management.ControlsListingController;
48 import com.android.systemui.dreams.DreamOverlayStateController;
49 import com.android.systemui.plugins.ActivityStarter;
50 import com.android.systemui.shared.condition.Monitor;
51 
52 import org.junit.Before;
53 import org.junit.Test;
54 import org.junit.runner.RunWith;
55 import org.mockito.ArgumentCaptor;
56 import org.mockito.Captor;
57 import org.mockito.Mock;
58 import org.mockito.MockitoAnnotations;
59 
60 import java.util.ArrayList;
61 import java.util.List;
62 import java.util.Optional;
63 
64 @SmallTest
65 @RunWith(AndroidTestingRunner.class)
66 public class DreamHomeControlsComplicationTest extends SysuiTestCase {
67     @Mock
68     private DreamHomeControlsComplication mComplication;
69 
70     @Mock
71     private DreamOverlayStateController mDreamOverlayStateController;
72 
73     @Mock
74     private Context mContext;
75 
76     @Mock
77     private Resources mResources;
78 
79     @Mock
80     private ControlsComponent mControlsComponent;
81 
82     @Mock
83     private ControlsController mControlsController;
84 
85     @Mock
86     private ControlsListingController mControlsListingController;
87 
88     @Mock
89     private DreamHomeControlsComplicationComponent.Factory mComponentFactory;
90 
91     @Captor
92     private ArgumentCaptor<ControlsListingController.ControlsListingCallback> mCallbackCaptor;
93 
94     @Mock
95     private LaunchableImageView mHomeControlsView;
96 
97     @Mock
98     private ActivityStarter mActivityStarter;
99 
100     @Mock
101     private UiEventLogger mUiEventLogger;
102 
103     @Captor
104     private ArgumentCaptor<DreamOverlayStateController.Callback> mStateCallbackCaptor;
105 
106     private Monitor mMonitor;
107 
108     @Before
setup()109     public void setup() {
110         MockitoAnnotations.initMocks(this);
111 
112         when(mContext.getString(anyInt())).thenReturn("");
113         when(mControlsComponent.getControlsController()).thenReturn(
114                 Optional.of(mControlsController));
115         when(mControlsComponent.getControlsListingController()).thenReturn(
116                 Optional.of(mControlsListingController));
117         when(mControlsComponent.getVisibility()).thenReturn(AVAILABLE);
118 
119         mMonitor = SelfExecutingMonitor.createInstance();
120     }
121 
122     @Test
complicationType()123     public void complicationType() {
124         final DreamHomeControlsComplication complication =
125                 new DreamHomeControlsComplication(mResources, mComponentFactory);
126         assertThat(complication.getRequiredTypeAvailability()).isEqualTo(
127                 COMPLICATION_TYPE_HOME_CONTROLS);
128     }
129 
130     @Test
complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication()131     public void complicationAvailability_serviceNotAvailable_noFavorites_doNotAddComplication() {
132         final DreamHomeControlsComplication.Registrant registrant =
133                 new DreamHomeControlsComplication.Registrant(mComplication,
134                         mDreamOverlayStateController, mControlsComponent, mMonitor);
135         registrant.start();
136 
137         setHaveFavorites(false);
138         setServiceAvailable(false);
139 
140         verify(mDreamOverlayStateController, never()).addComplication(mComplication);
141     }
142 
143     @Test
complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication()144     public void complicationAvailability_serviceAvailable_noFavorites_doNotAddComplication() {
145         final DreamHomeControlsComplication.Registrant registrant =
146                 new DreamHomeControlsComplication.Registrant(mComplication,
147                         mDreamOverlayStateController, mControlsComponent, mMonitor);
148         registrant.start();
149 
150         setHaveFavorites(false);
151         setServiceAvailable(true);
152 
153         verify(mDreamOverlayStateController, never()).addComplication(mComplication);
154     }
155 
156     @Test
complicationAvailability_serviceAvailable_noFavorites_panel_addComplication()157     public void complicationAvailability_serviceAvailable_noFavorites_panel_addComplication() {
158         final DreamHomeControlsComplication.Registrant registrant =
159                 new DreamHomeControlsComplication.Registrant(mComplication,
160                         mDreamOverlayStateController, mControlsComponent, mMonitor);
161         registrant.start();
162 
163         setHaveFavorites(false);
164         setServiceWithPanel();
165 
166         verify(mDreamOverlayStateController).addComplication(mComplication);
167     }
168 
169     @Test
complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication()170     public void complicationAvailability_serviceNotAvailable_haveFavorites_doNotAddComplication() {
171         final DreamHomeControlsComplication.Registrant registrant =
172                 new DreamHomeControlsComplication.Registrant(mComplication,
173                         mDreamOverlayStateController, mControlsComponent, mMonitor);
174         registrant.start();
175 
176         setHaveFavorites(true);
177         setServiceAvailable(false);
178 
179         verify(mDreamOverlayStateController, never()).addComplication(mComplication);
180     }
181 
182     @Test
complicationAvailability_serviceAvailable_haveFavorites_addComplication()183     public void complicationAvailability_serviceAvailable_haveFavorites_addComplication() {
184         final DreamHomeControlsComplication.Registrant registrant =
185                 new DreamHomeControlsComplication.Registrant(mComplication,
186                         mDreamOverlayStateController, mControlsComponent, mMonitor);
187         registrant.start();
188 
189         setHaveFavorites(true);
190         setServiceAvailable(true);
191 
192         verify(mDreamOverlayStateController).addComplication(mComplication);
193     }
194 
195     @Test
complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive()196     public void complicationAvailability_checkAvailabilityWhenDreamOverlayBecomesActive() {
197         final DreamHomeControlsComplication.Registrant registrant =
198                 new DreamHomeControlsComplication.Registrant(mComplication,
199                         mDreamOverlayStateController, mControlsComponent, mMonitor);
200         registrant.start();
201 
202         setServiceAvailable(true);
203         setHaveFavorites(false);
204 
205         // Complication not available on start.
206         verify(mDreamOverlayStateController, never()).addComplication(mComplication);
207 
208         // Favorite controls added, complication should be available now.
209         setHaveFavorites(true);
210 
211         // Dream overlay becomes active.
212         setDreamOverlayActive(true);
213 
214         // Verify complication is added.
215         verify(mDreamOverlayStateController).addComplication(mComplication);
216     }
217 
218     /**
219      * Ensures clicking home controls chip logs UiEvent.
220      */
221     @Test
testClick_logsUiEvent()222     public void testClick_logsUiEvent() {
223         final DreamHomeControlsComplication.DreamHomeControlsChipViewController viewController =
224                 new DreamHomeControlsComplication.DreamHomeControlsChipViewController(
225                         mHomeControlsView,
226                         mActivityStarter,
227                         mContext,
228                         mControlsComponent,
229                         mUiEventLogger);
230         viewController.onViewAttached();
231 
232         final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
233                 ArgumentCaptor.forClass(View.OnClickListener.class);
234         verify(mHomeControlsView).setOnClickListener(clickListenerCaptor.capture());
235 
236         clickListenerCaptor.getValue().onClick(mHomeControlsView);
237         verify(mUiEventLogger).log(DreamOverlayUiEvent.DREAM_HOME_CONTROLS_TAPPED);
238     }
239 
setHaveFavorites(boolean value)240     private void setHaveFavorites(boolean value) {
241         final List<StructureInfo> favorites = mock(List.class);
242         when(favorites.isEmpty()).thenReturn(!value);
243         when(mControlsController.getFavorites()).thenReturn(favorites);
244     }
245 
setServiceAvailable(boolean value)246     private void setServiceAvailable(boolean value) {
247         final List<ControlsServiceInfo> serviceInfos = mock(List.class);
248         when(mControlsListingController.getCurrentServices()).thenReturn(serviceInfos);
249         when(serviceInfos.isEmpty()).thenReturn(!value);
250         triggerControlsListingCallback(serviceInfos);
251     }
252 
setServiceWithPanel()253     private void setServiceWithPanel() {
254         final List<ControlsServiceInfo> serviceInfos = new ArrayList<>();
255         ControlsServiceInfo csi = mock(ControlsServiceInfo.class);
256         serviceInfos.add(csi);
257         when(csi.getPanelActivity()).thenReturn(new ComponentName("a", "b"));
258         when(mControlsListingController.getCurrentServices()).thenReturn(serviceInfos);
259         triggerControlsListingCallback(serviceInfos);
260     }
261 
setDreamOverlayActive(boolean value)262     private void setDreamOverlayActive(boolean value) {
263         when(mDreamOverlayStateController.isOverlayActive()).thenReturn(value);
264         verify(mDreamOverlayStateController).addCallback(mStateCallbackCaptor.capture());
265         mStateCallbackCaptor.getValue().onStateChanged();
266     }
267 
triggerControlsListingCallback(List<ControlsServiceInfo> serviceInfos)268     private void triggerControlsListingCallback(List<ControlsServiceInfo> serviceInfos) {
269         verify(mControlsListingController).addCallback(mCallbackCaptor.capture());
270         mCallbackCaptor.getValue().onServicesUpdated(serviceInfos);
271     }
272 }
273