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