1 /*
2  * Copyright (C) 2023 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.shade.carrier;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.mockito.ArgumentMatchers.any;
24 import static org.mockito.ArgumentMatchers.anyBoolean;
25 import static org.mockito.ArgumentMatchers.anyInt;
26 import static org.mockito.ArgumentMatchers.anyString;
27 import static org.mockito.ArgumentMatchers.eq;
28 import static org.mockito.Mockito.doAnswer;
29 import static org.mockito.Mockito.mock;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.reset;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.verifyZeroInteractions;
35 import static org.mockito.Mockito.when;
36 
37 import android.content.Context;
38 import android.content.Intent;
39 import android.os.Handler;
40 import android.provider.Settings;
41 import android.testing.AndroidTestingRunner;
42 import android.testing.TestableLooper;
43 import android.view.View;
44 import android.widget.TextView;
45 
46 import androidx.test.filters.SmallTest;
47 
48 import com.android.keyguard.CarrierTextManager;
49 import com.android.systemui.plugins.ActivityStarter;
50 import com.android.systemui.statusbar.connectivity.IconState;
51 import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
52 import com.android.systemui.statusbar.connectivity.NetworkController;
53 import com.android.systemui.statusbar.connectivity.SignalCallback;
54 import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
55 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
56 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
57 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger;
58 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
59 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
60 import com.android.systemui.util.CarrierConfigTracker;
61 import com.android.systemui.util.kotlin.FlowProviderKt;
62 import com.android.systemui.utils.leaks.LeakCheckedTest;
63 import com.android.systemui.utils.os.FakeHandler;
64 
65 import org.junit.Before;
66 import org.junit.Test;
67 import org.junit.runner.RunWith;
68 import org.mockito.ArgumentCaptor;
69 import org.mockito.Mock;
70 import org.mockito.MockitoAnnotations;
71 
72 import java.util.ArrayList;
73 import java.util.Arrays;
74 import java.util.List;
75 
76 import kotlinx.coroutines.flow.MutableStateFlow;
77 
78 @RunWith(AndroidTestingRunner.class)
79 @TestableLooper.RunWithLooper
80 @SmallTest
81 public class ShadeCarrierGroupControllerTest extends LeakCheckedTest {
82 
83     private static final String SINGLE_CARRIER_TEXT = "singleCarrierText";
84     private static final String MULTI_CARRIER_TEXT = "multiCarrierText";
85     private static final String FIRST_CARRIER_NAME = "carrier1";
86     private static final String SECOND_CARRIER_NAME = "carrier2";
87 
88     private ShadeCarrierGroupController mShadeCarrierGroupController;
89     private SignalCallback mSignalCallback;
90     private CarrierTextManager.CarrierTextCallback mCallback;
91     @Mock
92     private ShadeCarrierGroup mShadeCarrierGroup;
93     @Mock
94     private ActivityStarter mActivityStarter;
95     @Mock
96     private NetworkController mNetworkController;
97     @Mock
98     private CarrierTextManager.Builder mCarrierTextControllerBuilder;
99     @Mock
100     private CarrierTextManager mCarrierTextManager;
101     @Mock
102     private CarrierConfigTracker mCarrierConfigTracker;
103     @Mock
104     private ShadeCarrier mShadeCarrier1;
105     @Mock
106     private ShadeCarrier mShadeCarrier2;
107     @Mock
108     private ShadeCarrier mShadeCarrier3;
109     private TestableLooper mTestableLooper;
110     @Mock
111     private ShadeCarrierGroupController.OnSingleCarrierChangedListener
112             mOnSingleCarrierChangedListener;
113     @Mock
114     private MobileUiAdapter mMobileUiAdapter;
115     @Mock
116     private MobileIconsViewModel mMobileIconsViewModel;
117     @Mock
118     private ShadeCarrierGroupMobileIconViewModel mShadeCarrierGroupMobileIconViewModel;
119     @Mock
120     private MobileViewLogger mMobileViewLogger;
121     @Mock
122     private MobileContextProvider mMobileContextProvider;
123     @Mock
124     private StatusBarPipelineFlags mStatusBarPipelineFlags;
125 
126     private final MutableStateFlow<Boolean> mIsVisibleFlow =
127             FlowProviderKt.getMutableStateFlow(true);
128 
129     private FakeSlotIndexResolver mSlotIndexResolver;
130     private ClickListenerTextView mNoCarrierTextView;
131 
132     @Before
setup()133     public void setup() throws Exception {
134         MockitoAnnotations.initMocks(this);
135         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
136         mTestableLooper = TestableLooper.get(this);
137         Handler handler = new FakeHandler(TestableLooper.get(this).getLooper());
138 
139         when(mNetworkController.hasVoiceCallingFeature()).thenReturn(true);
140         doAnswer(invocation -> mSignalCallback = invocation.getArgument(0))
141                 .when(mNetworkController)
142                 .addCallback(any(SignalCallback.class));
143 
144         when(mCarrierTextControllerBuilder.setShowAirplaneMode(anyBoolean()))
145                 .thenReturn(mCarrierTextControllerBuilder);
146         when(mCarrierTextControllerBuilder.setShowMissingSim(anyBoolean()))
147                 .thenReturn(mCarrierTextControllerBuilder);
148         when(mCarrierTextControllerBuilder.setDebugLocationString(anyString()))
149                 .thenReturn(mCarrierTextControllerBuilder);
150         when(mCarrierTextControllerBuilder.build()).thenReturn(mCarrierTextManager);
151 
152         doAnswer(invocation -> mCallback = invocation.getArgument(0))
153                 .when(mCarrierTextManager)
154                 .setListening(any(CarrierTextManager.CarrierTextCallback.class));
155 
156         mNoCarrierTextView = new ClickListenerTextView(mContext);
157         when(mShadeCarrierGroup.getNoSimTextView()).thenReturn(mNoCarrierTextView);
158         when(mShadeCarrierGroup.getCarrier1View()).thenReturn(mShadeCarrier1);
159         when(mShadeCarrierGroup.getCarrier2View()).thenReturn(mShadeCarrier2);
160         when(mShadeCarrierGroup.getCarrier3View()).thenReturn(mShadeCarrier3);
161         when(mShadeCarrierGroup.getCarrierDivider1()).thenReturn(new View(mContext));
162         when(mShadeCarrierGroup.getCarrierDivider2()).thenReturn(new View(mContext));
163 
164         mSlotIndexResolver = new FakeSlotIndexResolver();
165 
166         when(mMobileUiAdapter.getMobileIconsViewModel()).thenReturn(mMobileIconsViewModel);
167 
168         mShadeCarrierGroupController = new ShadeCarrierGroupController.Builder(
169                 mActivityStarter,
170                 handler,
171                 TestableLooper.get(this).getLooper(),
172                 mNetworkController,
173                 mCarrierTextControllerBuilder,
174                 mContext,
175                 mCarrierConfigTracker,
176                 mSlotIndexResolver,
177                 mMobileUiAdapter,
178                 mMobileContextProvider,
179                 mStatusBarPipelineFlags
180         )
181                 .setShadeCarrierGroup(mShadeCarrierGroup)
182                 .build();
183 
184         mShadeCarrierGroupController.setListening(true);
185     }
186 
setupWithNewPipeline()187     private void setupWithNewPipeline() {
188         when(mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()).thenReturn(true);
189         when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
190         when(mMobileIconsViewModel.getLogger()).thenReturn(mMobileViewLogger);
191         when(mShadeCarrierGroupMobileIconViewModel.isVisible()).thenReturn(mIsVisibleFlow);
192         when(mMobileIconsViewModel.viewModelForSub(anyInt(), any()))
193                 .thenReturn(mShadeCarrierGroupMobileIconViewModel);
194     }
195 
196     @Test
testInitiallyMultiCarrier()197     public void testInitiallyMultiCarrier() {
198         assertFalse(mShadeCarrierGroupController.isSingleCarrier());
199     }
200 
201     @Test // throws no Exception
testUpdateCarrierText_sameLengths()202     public void testUpdateCarrierText_sameLengths() {
203         // listOfCarriers length 1, subscriptionIds length 1, anySims false
204         CarrierTextManager.CarrierTextCallbackInfo
205                 c1 = new CarrierTextManager.CarrierTextCallbackInfo(
206                 SINGLE_CARRIER_TEXT,
207                 new CharSequence[]{FIRST_CARRIER_NAME},
208                 false,
209                 new int[]{0});
210         mCallback.updateCarrierInfo(c1);
211 
212         // listOfCarriers length 1, subscriptionIds length 1, anySims true
213         CarrierTextManager.CarrierTextCallbackInfo
214                 c2 = new CarrierTextManager.CarrierTextCallbackInfo(
215                 SINGLE_CARRIER_TEXT,
216                 new CharSequence[]{FIRST_CARRIER_NAME},
217                 true,
218                 new int[]{0});
219         mCallback.updateCarrierInfo(c2);
220 
221         // listOfCarriers length 2, subscriptionIds length 2, anySims false
222         CarrierTextManager.CarrierTextCallbackInfo
223                 c3 = new CarrierTextManager.CarrierTextCallbackInfo(
224                 MULTI_CARRIER_TEXT,
225                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
226                 false,
227                 new int[]{0, 1});
228         mCallback.updateCarrierInfo(c3);
229 
230         // listOfCarriers length 2, subscriptionIds length 2, anySims true
231         CarrierTextManager.CarrierTextCallbackInfo
232                 c4 = new CarrierTextManager.CarrierTextCallbackInfo(
233                 MULTI_CARRIER_TEXT,
234                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
235                 true,
236                 new int[]{0, 1});
237         mCallback.updateCarrierInfo(c4);
238 
239         mTestableLooper.processAllMessages();
240     }
241 
242     @Test // throws no Exception
testUpdateCarrierText_differentLength()243     public void testUpdateCarrierText_differentLength() {
244         // listOfCarriers length 2, subscriptionIds length 1, anySims false
245         CarrierTextManager.CarrierTextCallbackInfo
246                 c1 = new CarrierTextManager.CarrierTextCallbackInfo(
247                 MULTI_CARRIER_TEXT,
248                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
249                 false,
250                 new int[]{0});
251         mCallback.updateCarrierInfo(c1);
252 
253         // listOfCarriers length 2, subscriptionIds length 1, anySims true
254         CarrierTextManager.CarrierTextCallbackInfo
255                 c2 = new CarrierTextManager.CarrierTextCallbackInfo(
256                 MULTI_CARRIER_TEXT,
257                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
258                 true,
259                 new int[]{0});
260         mCallback.updateCarrierInfo(c2);
261 
262         // listOfCarriers length 1, subscriptionIds length 2, anySims false
263         CarrierTextManager.CarrierTextCallbackInfo
264                 c3 = new CarrierTextManager.CarrierTextCallbackInfo(
265                 SINGLE_CARRIER_TEXT,
266                 new CharSequence[]{FIRST_CARRIER_NAME},
267                 false,
268                 new int[]{0, 1});
269         mCallback.updateCarrierInfo(c3);
270 
271         // listOfCarriers length 1, subscriptionIds length 2, anySims true
272         CarrierTextManager.CarrierTextCallbackInfo
273                 c4 = new CarrierTextManager.CarrierTextCallbackInfo(
274                 SINGLE_CARRIER_TEXT,
275                 new CharSequence[]{FIRST_CARRIER_NAME},
276                 true,
277                 new int[]{0, 1});
278         mCallback.updateCarrierInfo(c4);
279         mTestableLooper.processAllMessages();
280     }
281 
282     @Test // throws no Exception
testUpdateCarrierText_invalidSim()283     public void testUpdateCarrierText_invalidSim() {
284         mSlotIndexResolver.overrideInvalid = true;
285 
286         CarrierTextManager.CarrierTextCallbackInfo
287                 c4 = new CarrierTextManager.CarrierTextCallbackInfo(
288                 MULTI_CARRIER_TEXT,
289                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
290                 true,
291                 new int[]{0, 1});
292         mCallback.updateCarrierInfo(c4);
293         mTestableLooper.processAllMessages();
294     }
295 
296     @Test // throws no Exception
testSetMobileDataIndicators_invalidSim()297     public void testSetMobileDataIndicators_invalidSim() {
298         mSlotIndexResolver.overrideInvalid = true;
299 
300         MobileDataIndicators indicators = new MobileDataIndicators(
301                 mock(IconState.class),
302                 mock(IconState.class),
303                 0, 0, true, true, "", "", "", 0, true, true);
304         mSignalCallback.setMobileDataIndicators(indicators);
305     }
306 
307     @Test
testNoEmptyVisibleView_airplaneMode()308     public void testNoEmptyVisibleView_airplaneMode() {
309         CarrierTextManager.CarrierTextCallbackInfo
310                 info = new CarrierTextManager.CarrierTextCallbackInfo(
311                 "",
312                 new CharSequence[]{""},
313                 true,
314                 new int[]{0},
315                 true /* airplaneMode */);
316         mCallback.updateCarrierInfo(info);
317         mTestableLooper.processAllMessages();
318         assertEquals(View.GONE, mShadeCarrierGroup.getNoSimTextView().getVisibility());
319     }
320 
321     @Test
testVisibleView_airplaneMode_WFCOn()322     public void testVisibleView_airplaneMode_WFCOn() {
323         CarrierTextManager.CarrierTextCallbackInfo
324                 info = new CarrierTextManager.CarrierTextCallbackInfo(
325                 "",
326                 new CharSequence[]{FIRST_CARRIER_NAME, ""},
327                 true,
328                 new int[]{0, 1},
329                 false /* airplaneMode */);
330         mCallback.updateCarrierInfo(info);
331         mTestableLooper.processAllMessages();
332         assertEquals(View.VISIBLE, mShadeCarrierGroupController.getShadeCarrierVisibility(0));
333     }
334 
335     @Test
testListenerNotCalledOnRegistreation()336     public void testListenerNotCalledOnRegistreation() {
337         mShadeCarrierGroupController
338                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
339 
340         verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
341     }
342 
343     @Test
testSingleCarrier()344     public void testSingleCarrier() {
345         // Only one element in the info
346         CarrierTextManager.CarrierTextCallbackInfo
347                 info = new CarrierTextManager.CarrierTextCallbackInfo(
348                 SINGLE_CARRIER_TEXT,
349                 new CharSequence[]{SINGLE_CARRIER_TEXT},
350                 true,
351                 new int[]{0},
352                 false /* airplaneMode */);
353 
354         mCallback.updateCarrierInfo(info);
355         mTestableLooper.processAllMessages();
356 
357         verify(mShadeCarrier1).updateState(any(), eq(true));
358         verify(mShadeCarrier2).updateState(any(), eq(true));
359         verify(mShadeCarrier3).updateState(any(), eq(true));
360     }
361 
362     @Test
testMultiCarrier()363     public void testMultiCarrier() {
364         // More than one element in the info
365         CarrierTextManager.CarrierTextCallbackInfo
366                 info = new CarrierTextManager.CarrierTextCallbackInfo(
367                 MULTI_CARRIER_TEXT,
368                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
369                 true,
370                 new int[]{0, 1},
371                 false /* airplaneMode */);
372 
373         mCallback.updateCarrierInfo(info);
374         mTestableLooper.processAllMessages();
375 
376         verify(mShadeCarrier1).updateState(any(), eq(false));
377         verify(mShadeCarrier2).updateState(any(), eq(false));
378         verify(mShadeCarrier3).updateState(any(), eq(false));
379     }
380 
381     @Test
testSingleMultiCarrierSwitch()382     public void testSingleMultiCarrierSwitch() {
383         CarrierTextManager.CarrierTextCallbackInfo
384                 singleCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
385                 SINGLE_CARRIER_TEXT,
386                 new CharSequence[]{FIRST_CARRIER_NAME},
387                 true,
388                 new int[]{0},
389                 false /* airplaneMode */);
390 
391         CarrierTextManager.CarrierTextCallbackInfo
392                 multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
393                 MULTI_CARRIER_TEXT,
394                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
395                 true,
396                 new int[]{0, 1},
397                 false /* airplaneMode */);
398 
399         mCallback.updateCarrierInfo(singleCarrierInfo);
400         mTestableLooper.processAllMessages();
401 
402         mShadeCarrierGroupController
403                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
404         reset(mOnSingleCarrierChangedListener);
405 
406         mCallback.updateCarrierInfo(multiCarrierInfo);
407         mTestableLooper.processAllMessages();
408         verify(mOnSingleCarrierChangedListener).onSingleCarrierChanged(false);
409 
410         mCallback.updateCarrierInfo(singleCarrierInfo);
411         mTestableLooper.processAllMessages();
412         verify(mOnSingleCarrierChangedListener).onSingleCarrierChanged(true);
413     }
414 
415     @Test
testNoCallbackIfSingleCarrierDoesntChange()416     public void testNoCallbackIfSingleCarrierDoesntChange() {
417         CarrierTextManager.CarrierTextCallbackInfo
418                 singleCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
419                 SINGLE_CARRIER_TEXT,
420                 new CharSequence[]{FIRST_CARRIER_NAME},
421                 true,
422                 new int[]{0},
423                 false /* airplaneMode */);
424 
425         mCallback.updateCarrierInfo(singleCarrierInfo);
426         mTestableLooper.processAllMessages();
427 
428         mShadeCarrierGroupController
429                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
430 
431         mCallback.updateCarrierInfo(singleCarrierInfo);
432         mTestableLooper.processAllMessages();
433 
434         verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
435     }
436 
437     @Test
testNoCallbackIfMultiCarrierDoesntChange()438     public void testNoCallbackIfMultiCarrierDoesntChange() {
439         CarrierTextManager.CarrierTextCallbackInfo
440                 multiCarrierInfo = new CarrierTextManager.CarrierTextCallbackInfo(
441                 MULTI_CARRIER_TEXT,
442                 new CharSequence[]{FIRST_CARRIER_NAME, SECOND_CARRIER_NAME},
443                 true,
444                 new int[]{0, 1},
445                 false /* airplaneMode */);
446 
447         mCallback.updateCarrierInfo(multiCarrierInfo);
448         mTestableLooper.processAllMessages();
449 
450         mShadeCarrierGroupController
451                 .setOnSingleCarrierChangedListener(mOnSingleCarrierChangedListener);
452 
453         mCallback.updateCarrierInfo(multiCarrierInfo);
454         mTestableLooper.processAllMessages();
455 
456         verify(mOnSingleCarrierChangedListener, never()).onSingleCarrierChanged(anyBoolean());
457     }
458 
459     @TestableLooper.RunWithLooper(setAsMainLooper = true)
460     @Test
testUpdateModernMobileIcons_addSubscription()461     public void testUpdateModernMobileIcons_addSubscription() {
462         setupWithNewPipeline();
463 
464         mShadeCarrier1.setVisibility(View.GONE);
465         mShadeCarrier2.setVisibility(View.GONE);
466         mShadeCarrier3.setVisibility(View.GONE);
467 
468         List<Integer> subIds = new ArrayList<>();
469         subIds.add(0);
470         mShadeCarrierGroupController.updateModernMobileIcons(subIds);
471 
472         verify(mShadeCarrier1).addModernMobileView(any());
473         verify(mShadeCarrier2, never()).addModernMobileView(any());
474         verify(mShadeCarrier3, never()).addModernMobileView(any());
475 
476         resetShadeCarriers();
477 
478         subIds.add(1);
479         mShadeCarrierGroupController.updateModernMobileIcons(subIds);
480 
481         verify(mShadeCarrier1, times(1)).removeModernMobileView();
482 
483         verify(mShadeCarrier1).addModernMobileView(any());
484         verify(mShadeCarrier2).addModernMobileView(any());
485         verify(mShadeCarrier3, never()).addModernMobileView(any());
486     }
487 
488     @TestableLooper.RunWithLooper(setAsMainLooper = true)
489     @Test
testUpdateModernMobileIcons_removeSubscription()490     public void testUpdateModernMobileIcons_removeSubscription() {
491         setupWithNewPipeline();
492 
493         List<Integer> subIds = new ArrayList<>();
494         subIds.add(0);
495         subIds.add(1);
496         mShadeCarrierGroupController.updateModernMobileIcons(subIds);
497 
498         verify(mShadeCarrier1).addModernMobileView(any());
499         verify(mShadeCarrier2).addModernMobileView(any());
500         verify(mShadeCarrier3, never()).addModernMobileView(any());
501 
502         resetShadeCarriers();
503 
504         subIds.remove(1);
505         mShadeCarrierGroupController.updateModernMobileIcons(subIds);
506 
507         verify(mShadeCarrier1, times(1)).removeModernMobileView();
508         verify(mShadeCarrier2, times(1)).removeModernMobileView();
509 
510         verify(mShadeCarrier1).addModernMobileView(any());
511         verify(mShadeCarrier2, never()).addModernMobileView(any());
512         verify(mShadeCarrier3, never()).addModernMobileView(any());
513     }
514 
515     @TestableLooper.RunWithLooper(setAsMainLooper = true)
516     @Test
testUpdateModernMobileIcons_removeSubscriptionOutOfOrder()517     public void testUpdateModernMobileIcons_removeSubscriptionOutOfOrder() {
518         setupWithNewPipeline();
519 
520         List<Integer> subIds = new ArrayList<>();
521         subIds.add(0);
522         subIds.add(1);
523         subIds.add(2);
524         mShadeCarrierGroupController.updateModernMobileIcons(subIds);
525 
526         verify(mShadeCarrier1).addModernMobileView(any());
527         verify(mShadeCarrier2).addModernMobileView(any());
528         verify(mShadeCarrier3).addModernMobileView(any());
529 
530         resetShadeCarriers();
531 
532         subIds.remove(1);
533         mShadeCarrierGroupController.updateModernMobileIcons(subIds);
534 
535         verify(mShadeCarrier1).removeModernMobileView();
536         verify(mShadeCarrier2).removeModernMobileView();
537         verify(mShadeCarrier3).removeModernMobileView();
538 
539         verify(mShadeCarrier1).addModernMobileView(any());
540         verify(mShadeCarrier2, never()).addModernMobileView(any());
541         verify(mShadeCarrier3).addModernMobileView(any());
542     }
543 
544     @TestableLooper.RunWithLooper(setAsMainLooper = true)
545     @Test
testProcessSubIdList_moreSubsThanSimSlots_listLimitedToMax()546     public void testProcessSubIdList_moreSubsThanSimSlots_listLimitedToMax() {
547         setupWithNewPipeline();
548 
549         List<Integer> subIds = Arrays.asList(0, 1, 2, 2);
550 
551         assertThat(mShadeCarrierGroupController.processSubIdList(subIds).size()).isEqualTo(3);
552     }
553 
554     @TestableLooper.RunWithLooper(setAsMainLooper = true)
555     @Test
testProcessSubIdList_invalidSimSlotIndexFilteredOut()556     public void testProcessSubIdList_invalidSimSlotIndexFilteredOut() {
557         setupWithNewPipeline();
558 
559         List<Integer> subIds = Arrays.asList(0, 1, -1);
560 
561         List<ShadeCarrierGroupController.IconData> processedSubs =
562                 mShadeCarrierGroupController.processSubIdList(subIds);
563         assertThat(processedSubs).hasSize(2);
564         assertThat(processedSubs.get(0).subId).isNotEqualTo(-1);
565         assertThat(processedSubs.get(1).subId).isNotEqualTo(-1);
566     }
567 
568     @TestableLooper.RunWithLooper(setAsMainLooper = true)
569     @Test
testProcessSubIdList_indexGreaterThanSimSlotsFilteredOut()570     public void testProcessSubIdList_indexGreaterThanSimSlotsFilteredOut() {
571         setupWithNewPipeline();
572 
573         List<Integer> subIds = Arrays.asList(0, 4);
574 
575         List<ShadeCarrierGroupController.IconData> processedSubs =
576                 mShadeCarrierGroupController.processSubIdList(subIds);
577         assertThat(processedSubs).hasSize(1);
578         assertThat(processedSubs.get(0).subId).isNotEqualTo(4);
579     }
580 
581 
582     @Test
testOnlyInternalViewsHaveClickableListener()583     public void testOnlyInternalViewsHaveClickableListener() {
584         ArgumentCaptor<View.OnClickListener> captor =
585                 ArgumentCaptor.forClass(View.OnClickListener.class);
586 
587         verify(mShadeCarrier1).setOnClickListener(captor.capture());
588         verify(mShadeCarrier2).setOnClickListener(captor.getValue());
589         verify(mShadeCarrier3).setOnClickListener(captor.getValue());
590 
591         assertThat(mNoCarrierTextView.getOnClickListener()).isSameInstanceAs(captor.getValue());
592         verify(mShadeCarrierGroup, never()).setOnClickListener(any());
593     }
594 
595     @Test
testOnClickListenerDoesntStartActivityIfViewNotVisible()596     public void testOnClickListenerDoesntStartActivityIfViewNotVisible() {
597         ArgumentCaptor<View.OnClickListener> captor =
598                 ArgumentCaptor.forClass(View.OnClickListener.class);
599 
600         verify(mShadeCarrier1).setOnClickListener(captor.capture());
601         when(mShadeCarrier1.isVisibleToUser()).thenReturn(false);
602 
603         captor.getValue().onClick(mShadeCarrier1);
604         verifyZeroInteractions(mActivityStarter);
605     }
606 
607     @Test
testOnClickListenerLaunchesActivityIfViewVisible()608     public void testOnClickListenerLaunchesActivityIfViewVisible() {
609         ArgumentCaptor<View.OnClickListener> listenerCaptor =
610                 ArgumentCaptor.forClass(View.OnClickListener.class);
611         ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
612 
613         verify(mShadeCarrier1).setOnClickListener(listenerCaptor.capture());
614         when(mShadeCarrier1.isVisibleToUser()).thenReturn(true);
615 
616         listenerCaptor.getValue().onClick(mShadeCarrier1);
617         verify(mActivityStarter)
618                 .postStartActivityDismissingKeyguard(intentCaptor.capture(), anyInt());
619         assertThat(intentCaptor.getValue().getAction())
620                 .isEqualTo(Settings.ACTION_WIRELESS_SETTINGS);
621     }
622 
resetShadeCarriers()623     private void resetShadeCarriers() {
624         reset(mShadeCarrier1);
625         reset(mShadeCarrier2);
626         reset(mShadeCarrier3);
627     }
628 
629     private class FakeSlotIndexResolver implements ShadeCarrierGroupController.SlotIndexResolver {
630         public boolean overrideInvalid;
631 
632         @Override
getSlotIndex(int subscriptionId)633         public int getSlotIndex(int subscriptionId) {
634             return overrideInvalid ? -1 : subscriptionId;
635         }
636     }
637 
638     private class ClickListenerTextView extends TextView {
639         View.OnClickListener mListener = null;
640 
ClickListenerTextView(Context context)641         ClickListenerTextView(Context context) {
642             super(context);
643         }
644 
645         @Override
setOnClickListener(OnClickListener l)646         public void setOnClickListener(OnClickListener l) {
647             super.setOnClickListener(l);
648             mListener = l;
649         }
650 
getOnClickListener()651         View.OnClickListener getOnClickListener() {
652             return mListener;
653         }
654     }
655 }
656