• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX;
20 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_YES;
21 
22 import android.annotation.MainThread;
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.os.Handler;
28 import android.os.Looper;
29 import android.os.Message;
30 import android.provider.Settings;
31 import android.telephony.SubscriptionManager;
32 import android.text.TextUtils;
33 import android.util.Log;
34 import android.view.View;
35 import android.widget.TextView;
36 
37 import androidx.annotation.VisibleForTesting;
38 
39 import com.android.keyguard.CarrierTextManager;
40 import com.android.settingslib.AccessibilityContentDescriptions;
41 import com.android.settingslib.mobile.TelephonyIcons;
42 import com.android.systemui.Flags;
43 import com.android.systemui.dagger.SysUISingleton;
44 import com.android.systemui.dagger.qualifiers.Application;
45 import com.android.systemui.dagger.qualifiers.Background;
46 import com.android.systemui.dagger.qualifiers.Main;
47 import com.android.systemui.kairos.ExperimentalKairosApi;
48 import com.android.systemui.kairos.KairosNetwork;
49 import com.android.systemui.plugins.ActivityStarter;
50 import com.android.systemui.res.R;
51 import com.android.systemui.shade.ShadeDisplayAware;
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.phone.StatusBarLocation;
57 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
58 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter;
59 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos;
60 import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder;
61 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView;
62 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
63 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos;
64 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel;
65 import com.android.systemui.util.CarrierConfigTracker;
66 
67 import dagger.Lazy;
68 
69 import kotlin.OptIn;
70 import kotlin.Pair;
71 
72 import kotlinx.coroutines.CoroutineScope;
73 import kotlinx.coroutines.Job;
74 
75 import java.util.ArrayList;
76 import java.util.List;
77 import java.util.concurrent.CancellationException;
78 import java.util.function.Consumer;
79 
80 import javax.inject.Inject;
81 
82 @OptIn(markerClass = ExperimentalKairosApi.class)
83 public class ShadeCarrierGroupController {
84     private static final String TAG = "ShadeCarrierGroup";
85 
86     /**
87      * Support up to 3 slots which is what's supported by {@link TelephonyManager#getPhoneCount}
88      */
89     private static final int SIM_SLOTS = 3;
90 
91     private final ActivityStarter mActivityStarter;
92     private final Handler mBgHandler;
93     private final Context mContext;
94     private final NetworkController mNetworkController;
95     private final CarrierTextManager mCarrierTextManager;
96     private final TextView mNoSimTextView;
97     // Non final for testing
98     private H mMainHandler;
99     private final Callback mCallback;
100     private final MobileIconsViewModel mMobileIconsViewModel;
101     private final MobileContextProvider mMobileContextProvider;
102     private final StatusBarPipelineFlags mStatusBarPipelineFlags;
103     private boolean mListening;
104     private final CellSignalState[] mInfos =
105             new CellSignalState[SIM_SLOTS];
106     private View[] mCarrierDividers = new View[SIM_SLOTS - 1];
107     private ShadeCarrier[] mCarrierGroups = new ShadeCarrier[SIM_SLOTS];
108     private int[] mLastSignalLevel = new int[SIM_SLOTS];
109     private String[] mLastSignalLevelDescription = new String[SIM_SLOTS];
110     private final CarrierConfigTracker mCarrierConfigTracker;
111 
112     private boolean mIsSingleCarrier;
113     @Nullable
114     private OnSingleCarrierChangedListener mOnSingleCarrierChangedListener;
115 
116     private final SlotIndexResolver mSlotIndexResolver;
117 
118     private final ShadeCarrierGroupControllerLogger mLogger;
119 
120     private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
121     private final CoroutineScope mAppScope;
122     private final KairosNetwork mKairosNetwork;
123 
124     private final SignalCallback mSignalCallback = new SignalCallback() {
125         @Override
126         public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) {
127             int slotIndex = getSlotIndex(indicators.subId);
128             if (slotIndex >= SIM_SLOTS) {
129                 Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
130                 return;
131             }
132             if (slotIndex == INVALID_SIM_SLOT_INDEX) {
133                 Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId);
134                 return;
135             }
136             mInfos[slotIndex] = new CellSignalState(
137                     indicators.statusIcon.visible,
138                     indicators.statusIcon.icon,
139                     indicators.statusIcon.contentDescription,
140                     indicators.typeContentDescription.toString(),
141                     indicators.roaming
142             );
143             mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
144         }
145 
146         @Override
147         public void setNoSims(boolean hasNoSims, boolean simDetected) {
148             if (hasNoSims) {
149                 for (int i = 0; i < SIM_SLOTS; i++) {
150                     mInfos[i] = mInfos[i].changeVisibility(false);
151                 }
152             }
153             mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
154         }
155     };
156     private final ArrayList<Job> mBindingJobs = new ArrayList<>();
157 
158     private static class Callback implements CarrierTextManager.CarrierTextCallback {
159         private H mHandler;
160 
Callback(H handler)161         Callback(H handler) {
162             mHandler = handler;
163         }
164 
165         @Override
updateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info)166         public void updateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info) {
167             mHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
168         }
169     }
170 
ShadeCarrierGroupController( ShadeCarrierGroup view, ActivityStarter activityStarter, @Background Handler bgHandler, @Main Looper mainLooper, ShadeCarrierGroupControllerLogger logger, NetworkController networkController, CarrierTextManager.Builder carrierTextManagerBuilder, @ShadeDisplayAware Context context, CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider, StatusBarPipelineFlags statusBarPipelineFlags, Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, CoroutineScope appScope, KairosNetwork kairosNetwork )171     private ShadeCarrierGroupController(
172             ShadeCarrierGroup view,
173             ActivityStarter activityStarter,
174             @Background Handler bgHandler,
175             @Main Looper mainLooper,
176             ShadeCarrierGroupControllerLogger logger,
177             NetworkController networkController,
178             CarrierTextManager.Builder carrierTextManagerBuilder,
179             @ShadeDisplayAware Context context,
180             CarrierConfigTracker carrierConfigTracker,
181             SlotIndexResolver slotIndexResolver,
182             MobileUiAdapter mobileUiAdapter,
183             MobileContextProvider mobileContextProvider,
184             StatusBarPipelineFlags statusBarPipelineFlags,
185             Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos,
186             CoroutineScope appScope,
187             KairosNetwork kairosNetwork
188     ) {
189         mContext = context;
190         mActivityStarter = activityStarter;
191         mBgHandler = bgHandler;
192         mLogger = logger;
193         mNetworkController = networkController;
194         mStatusBarPipelineFlags = statusBarPipelineFlags;
195         mCarrierTextManager = carrierTextManagerBuilder
196                 .setShowAirplaneMode(false)
197                 .setShowMissingSim(false)
198                 .setDebugLocationString("Shade")
199                 .build();
200         mCarrierConfigTracker = carrierConfigTracker;
201         mSlotIndexResolver = slotIndexResolver;
202         mMobileUiAdapterKairos = mobileUiAdapterKairos;
203         mAppScope = appScope;
204         mKairosNetwork = kairosNetwork;
205         View.OnClickListener onClickListener = v -> {
206             if (!v.isVisibleToUser()) {
207                 return;
208             }
209             mActivityStarter.postStartActivityDismissingKeyguard(
210                     new Intent(Settings.ACTION_WIRELESS_SETTINGS), 0);
211         };
212 
213         mNoSimTextView = view.getNoSimTextView();
214         mNoSimTextView.setOnClickListener(onClickListener);
215         mMainHandler = new H(mainLooper, this::handleUpdateCarrierInfo, this::handleUpdateState);
216         mCallback = new Callback(mMainHandler);
217 
218         mCarrierGroups[0] = view.getCarrier1View();
219         mCarrierGroups[1] = view.getCarrier2View();
220         mCarrierGroups[2] = view.getCarrier3View();
221 
222         mMobileContextProvider = mobileContextProvider;
223         mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
224 
225         if (mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
226             if (Flags.statusBarMobileIconKairos()) {
227                 mobileUiAdapterKairos.get().setShadeCarrierGroupController(this);
228             } else {
229                 mobileUiAdapter.setShadeCarrierGroupController(this);
230                 MobileIconsBinder.bind(view, mMobileIconsViewModel);
231             }
232         }
233 
234         mCarrierDividers[0] = view.getCarrierDivider1();
235         mCarrierDividers[1] = view.getCarrierDivider2();
236 
237         for (int i = 0; i < SIM_SLOTS; i++) {
238             mInfos[i] = new CellSignalState(
239                     false,
240                     R.drawable.ic_shade_no_calling_sms,
241                     context.getText(AccessibilityContentDescriptions.NO_CALLING).toString(),
242                     "",
243                     false);
244             mLastSignalLevel[i] = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
245             mLastSignalLevelDescription[i] =
246                     context.getText(AccessibilityContentDescriptions.PHONE_SIGNAL_STRENGTH[0])
247                             .toString();
248             mCarrierGroups[i].setOnClickListener(onClickListener);
249         }
250         mIsSingleCarrier = computeIsSingleCarrier();
251         view.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
252 
253         view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
254             @Override
255             public void onViewAttachedToWindow(View v) {
256             }
257 
258             @Override
259             public void onViewDetachedFromWindow(View v) {
260                 setListening(false);
261             }
262         });
263     }
264 
265     /** Updates the number of visible mobile icons using the new pipeline. */
updateModernMobileIcons(List<Integer> subIds)266     public void updateModernMobileIcons(List<Integer> subIds) {
267         if (!mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
268             Log.d(TAG, "ignoring new pipeline callback because new mobile icon is disabled");
269             return;
270         }
271 
272         for (ShadeCarrier carrier : mCarrierGroups) {
273             carrier.removeModernMobileView();
274         }
275 
276         List<IconData> iconDataList = processSubIdList(subIds);
277 
278         if (Flags.statusBarMobileIconKairos()) {
279             for (Job job : mBindingJobs) {
280                 job.cancel(new CancellationException());
281             }
282             mBindingJobs.clear();
283             MobileIconsViewModelKairos mobileIconsViewModel =
284                     mMobileUiAdapterKairos.get().getMobileIconsViewModel();
285             for (IconData iconData : iconDataList) {
286                 ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex];
287 
288                 Context mobileContext =
289                         mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext);
290 
291                 Pair<ModernShadeCarrierGroupMobileView, Job> viewAndJob =
292                         ModernShadeCarrierGroupMobileView.constructAndBind(
293                                 mobileContext,
294                                 mobileIconsViewModel.getLogger(),
295                                 "mobile_carrier_shade_group",
296                                 mobileIconsViewModel.shadeCarrierGroupIcon(iconData.subId),
297                                 mAppScope,
298                                 iconData.subId,
299                                 StatusBarLocation.SHADE_CARRIER_GROUP,
300                                 mKairosNetwork
301                         );
302                 mBindingJobs.add(viewAndJob.getSecond());
303                 carrier.addModernMobileView(viewAndJob.getFirst());
304             }
305         } else {
306             for (IconData iconData : iconDataList) {
307                 ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex];
308 
309                 Context mobileContext =
310                         mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext);
311 
312                 ModernShadeCarrierGroupMobileView modernMobileView =
313                         ModernShadeCarrierGroupMobileView
314                                 .constructAndBind(
315                                         mobileContext,
316                                         mMobileIconsViewModel.getLogger(),
317                                         "mobile_carrier_shade_group",
318                                         (ShadeCarrierGroupMobileIconViewModel) mMobileIconsViewModel
319                                                 .viewModelForSub(iconData.subId,
320                                                         StatusBarLocation.SHADE_CARRIER_GROUP)
321                                 );
322                 carrier.addModernMobileView(modernMobileView);
323             }
324         }
325     }
326 
327     @VisibleForTesting
processSubIdList(List<Integer> subIds)328     List<IconData> processSubIdList(List<Integer> subIds) {
329         return subIds
330                 .stream()
331                 .limit(SIM_SLOTS)
332                 .map(subId -> new IconData(subId, getSlotIndex(subId)))
333                 .filter(iconData ->
334                         iconData.slotIndex < SIM_SLOTS
335                                 && iconData.slotIndex != INVALID_SIM_SLOT_INDEX
336                 )
337                 .toList();
338     }
339 
340     @VisibleForTesting
getSlotIndex(int subscriptionId)341     protected int getSlotIndex(int subscriptionId) {
342         return mSlotIndexResolver.getSlotIndex(subscriptionId);
343     }
344 
345     @VisibleForTesting
getShadeCarrierVisibility(int index)346     protected int getShadeCarrierVisibility(int index) {
347         return mCarrierGroups[index].getVisibility();
348     }
349 
350     /**
351      * Sets a {@link OnSingleCarrierChangedListener}.
352      *
353      * This will get notified when the number of carriers changes between 1 and "not one".
354      */
setOnSingleCarrierChangedListener( @ullable OnSingleCarrierChangedListener listener)355     public void setOnSingleCarrierChangedListener(
356             @Nullable OnSingleCarrierChangedListener listener) {
357         mOnSingleCarrierChangedListener = listener;
358     }
359 
isSingleCarrier()360     public boolean isSingleCarrier() {
361         return mIsSingleCarrier;
362     }
363 
computeIsSingleCarrier()364     private boolean computeIsSingleCarrier() {
365         int carrierCount = 0;
366         for (int i = 0; i < SIM_SLOTS; i++) {
367 
368             if (mInfos[i].visible) {
369                 carrierCount++;
370             }
371         }
372         return carrierCount == 1;
373     }
374 
setListening(boolean listening)375     public void setListening(boolean listening) {
376         if (listening == mListening) {
377             return;
378         }
379         mListening = listening;
380 
381         mBgHandler.post(this::updateListeners);
382     }
383 
updateListeners()384     private void updateListeners() {
385         if (mListening) {
386             if (mNetworkController.hasVoiceCallingFeature()) {
387                 mNetworkController.addCallback(mSignalCallback);
388             }
389             mCarrierTextManager.setListening(mCallback);
390         } else {
391             mNetworkController.removeCallback(mSignalCallback);
392             mCarrierTextManager.setListening(null);
393         }
394     }
395 
396 
397     @MainThread
handleUpdateState()398     private void handleUpdateState() {
399         if (!mMainHandler.getLooper().isCurrentThread()) {
400             mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget();
401             return;
402         }
403 
404         boolean singleCarrier = computeIsSingleCarrier();
405 
406         if (singleCarrier) {
407             for (int i = 0; i < SIM_SLOTS; i++) {
408                 if (mInfos[i].visible
409                         && mInfos[i].mobileSignalIconId == R.drawable.ic_shade_sim_card) {
410                     mInfos[i] = new CellSignalState(true, R.drawable.ic_blank, "", "", false);
411                 }
412             }
413         }
414 
415         if (!mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) {
416             for (int i = 0; i < SIM_SLOTS; i++) {
417                 mCarrierGroups[i].updateState(mInfos[i], singleCarrier);
418             }
419         }
420 
421         mCarrierDividers[0].setVisibility(
422                 mInfos[0].visible && mInfos[1].visible ? View.VISIBLE : View.GONE);
423         // This tackles the case of slots 2 being available as well as at least one other.
424         // In that case we show the second divider. Note that if both dividers are visible, it means
425         // all three slots are in use, and that is correct.
426         mCarrierDividers[1].setVisibility(
427                 (mInfos[1].visible && mInfos[2].visible)
428                         || (mInfos[0].visible && mInfos[2].visible) ? View.VISIBLE : View.GONE);
429         if (mIsSingleCarrier != singleCarrier) {
430             mIsSingleCarrier = singleCarrier;
431             if (mOnSingleCarrierChangedListener != null) {
432                 mOnSingleCarrierChangedListener.onSingleCarrierChanged(singleCarrier);
433             }
434         }
435     }
436 
437     @MainThread
handleUpdateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info)438     private void handleUpdateCarrierInfo(CarrierTextManager.CarrierTextCallbackInfo info) {
439         if (!mMainHandler.getLooper().isCurrentThread()) {
440             mMainHandler.obtainMessage(H.MSG_UPDATE_CARRIER_INFO, info).sendToTarget();
441             return;
442         }
443 
444         mLogger.logHandleUpdateCarrierInfo(info);
445 
446         mNoSimTextView.setVisibility(View.GONE);
447         if (info.isInSatelliteMode) {
448             mLogger.logUsingSatelliteText(info.carrierText);
449             showSingleText(info.carrierText);
450         } else if (!info.airplaneMode && info.anySimReady) {
451             boolean[] slotSeen = new boolean[SIM_SLOTS];
452             if (info.listOfCarriers.length == info.subscriptionIds.length) {
453                 mLogger.logUsingSimViews();
454                 for (int i = 0; i < SIM_SLOTS && i < info.listOfCarriers.length; i++) {
455                     int slot = getSlotIndex(info.subscriptionIds[i]);
456                     if (slot >= SIM_SLOTS) {
457                         Log.w(TAG, "updateInfoCarrier - slot: " + slot);
458                         continue;
459                     }
460                     if (slot == INVALID_SIM_SLOT_INDEX) {
461                         Log.e(TAG,
462                                 "Invalid SIM slot index for subscription: "
463                                         + info.subscriptionIds[i]);
464                         continue;
465                     }
466                     String carrierText = info.listOfCarriers[i].toString().trim();
467                     if (!TextUtils.isEmpty(carrierText)) {
468                         mInfos[slot] = mInfos[slot].changeVisibility(true);
469                         slotSeen[slot] = true;
470                         mCarrierGroups[slot].setCarrierText(carrierText);
471                         mCarrierGroups[slot].setVisibility(View.VISIBLE);
472                     }
473                 }
474                 for (int i = 0; i < SIM_SLOTS; i++) {
475                     if (!slotSeen[i]) {
476                         mInfos[i] = mInfos[i].changeVisibility(false);
477                         mCarrierGroups[i].setVisibility(View.GONE);
478                     }
479                 }
480             } else {
481                 mLogger.logInvalidArrayLengths(
482                         info.listOfCarriers.length, info.subscriptionIds.length);
483             }
484         } else {
485             // No sims or airplane mode (but not WFC), so just show the main carrier text.
486             mLogger.logUsingNoSimView(info.carrierText);
487             showSingleText(info.carrierText);
488         }
489         handleUpdateState(); // handleUpdateCarrierInfo is always called from main thread.
490     }
491 
492     /**
493      * Shows only the given text in a single TextView and hides ShadeCarrierGroup (which would show
494      * individual SIM details).
495      */
showSingleText(CharSequence text)496     private void showSingleText(CharSequence text) {
497         for (int i = 0; i < SIM_SLOTS; i++) {
498             mInfos[i] = mInfos[i].changeVisibility(false);
499             mCarrierGroups[i].setCarrierText("");
500             mCarrierGroups[i].setVisibility(View.GONE);
501         }
502         // TODO(b/341841138): Re-name this view now that it's being used for more than just the
503         //  no-SIM case.
504         mNoSimTextView.setText(text);
505         if (!TextUtils.isEmpty(text)) {
506             mNoSimTextView.setVisibility(View.VISIBLE);
507         }
508     }
509 
510     private static class H extends Handler {
511         private Consumer<CarrierTextManager.CarrierTextCallbackInfo> mUpdateCarrierInfo;
512         private Runnable mUpdateState;
513         static final int MSG_UPDATE_CARRIER_INFO = 0;
514         static final int MSG_UPDATE_STATE = 1;
515 
H(Looper looper, Consumer<CarrierTextManager.CarrierTextCallbackInfo> updateCarrierInfo, Runnable updateState)516         H(Looper looper,
517                 Consumer<CarrierTextManager.CarrierTextCallbackInfo> updateCarrierInfo,
518                 Runnable updateState) {
519             super(looper);
520             mUpdateCarrierInfo = updateCarrierInfo;
521             mUpdateState = updateState;
522         }
523 
524         @Override
handleMessage(Message msg)525         public void handleMessage(Message msg) {
526             switch (msg.what) {
527                 case MSG_UPDATE_CARRIER_INFO:
528                     mUpdateCarrierInfo.accept(
529                             (CarrierTextManager.CarrierTextCallbackInfo) msg.obj);
530                     break;
531                 case MSG_UPDATE_STATE:
532                     mUpdateState.run();
533                     break;
534                 default:
535                     super.handleMessage(msg);
536             }
537         }
538     }
539 
540     public static class Builder {
541         private ShadeCarrierGroup mView;
542         private final ActivityStarter mActivityStarter;
543         private final Handler mHandler;
544         private final Looper mLooper;
545         private final ShadeCarrierGroupControllerLogger mLogger;
546         private final NetworkController mNetworkController;
547         private final CarrierTextManager.Builder mCarrierTextControllerBuilder;
548         private final Context mContext;
549         private final CarrierConfigTracker mCarrierConfigTracker;
550         private final SlotIndexResolver mSlotIndexResolver;
551         private final MobileUiAdapter mMobileUiAdapter;
552         private final MobileContextProvider mMobileContextProvider;
553         private final StatusBarPipelineFlags mStatusBarPipelineFlags;
554         private final CoroutineScope mAppScope;
555         private final KairosNetwork mKairosNetwork;
556         private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos;
557 
558         @Inject
Builder( ActivityStarter activityStarter, @Background Handler handler, @Main Looper looper, ShadeCarrierGroupControllerLogger logger, NetworkController networkController, CarrierTextManager.Builder carrierTextControllerBuilder, @ShadeDisplayAware Context context, CarrierConfigTracker carrierConfigTracker, SlotIndexResolver slotIndexResolver, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider, StatusBarPipelineFlags statusBarPipelineFlags, @Application CoroutineScope appScope, KairosNetwork kairosNetwork, Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos )559         public Builder(
560                 ActivityStarter activityStarter,
561                 @Background Handler handler,
562                 @Main Looper looper,
563                 ShadeCarrierGroupControllerLogger logger,
564                 NetworkController networkController,
565                 CarrierTextManager.Builder carrierTextControllerBuilder,
566                 @ShadeDisplayAware Context context,
567                 CarrierConfigTracker carrierConfigTracker,
568                 SlotIndexResolver slotIndexResolver,
569                 MobileUiAdapter mobileUiAdapter,
570                 MobileContextProvider mobileContextProvider,
571                 StatusBarPipelineFlags statusBarPipelineFlags,
572                 @Application CoroutineScope appScope,
573                 KairosNetwork kairosNetwork,
574                 Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos
575         ) {
576             mActivityStarter = activityStarter;
577             mHandler = handler;
578             mLooper = looper;
579             mLogger = logger;
580             mNetworkController = networkController;
581             mCarrierTextControllerBuilder = carrierTextControllerBuilder;
582             mContext = context;
583             mCarrierConfigTracker = carrierConfigTracker;
584             mSlotIndexResolver = slotIndexResolver;
585             mMobileUiAdapter = mobileUiAdapter;
586             mMobileContextProvider = mobileContextProvider;
587             mStatusBarPipelineFlags = statusBarPipelineFlags;
588             mAppScope = appScope;
589             mKairosNetwork = kairosNetwork;
590             mMobileUiAdapterKairos = mobileUiAdapterKairos;
591         }
592 
setShadeCarrierGroup(ShadeCarrierGroup view)593         public Builder setShadeCarrierGroup(ShadeCarrierGroup view) {
594             mView = view;
595             return this;
596         }
597 
build()598         public ShadeCarrierGroupController build() {
599             return new ShadeCarrierGroupController(
600                     mView,
601                     mActivityStarter,
602                     mHandler,
603                     mLooper,
604                     mLogger,
605                     mNetworkController,
606                     mCarrierTextControllerBuilder,
607                     mContext,
608                     mCarrierConfigTracker,
609                     mSlotIndexResolver,
610                     mMobileUiAdapter,
611                     mMobileContextProvider,
612                     mStatusBarPipelineFlags,
613                     mMobileUiAdapterKairos,
614                     mAppScope,
615                     mKairosNetwork);
616         }
617     }
618 
619     /**
620      * Notify when the state changes from 1 carrier to "not one" and viceversa
621      */
622     @FunctionalInterface
623     public interface OnSingleCarrierChangedListener {
onSingleCarrierChanged(boolean isSingleCarrier)624         void onSingleCarrierChanged(boolean isSingleCarrier);
625     }
626 
627     /**
628      * Interface for resolving slot index from subscription ID.
629      */
630     @FunctionalInterface
631     public interface SlotIndexResolver {
632         /**
633          * Get slot index for given sub id.
634          */
getSlotIndex(int subscriptionId)635         int getSlotIndex(int subscriptionId);
636     }
637 
638     /**
639      * Default implementation for {@link SlotIndexResolver}.
640      *
641      * It retrieves the slot index using {@link SubscriptionManager#getSlotIndex}.
642      */
643     @SysUISingleton
644     public static class SubscriptionManagerSlotIndexResolver implements SlotIndexResolver {
645 
646         @Inject
SubscriptionManagerSlotIndexResolver()647         public SubscriptionManagerSlotIndexResolver() {
648         }
649 
650         @Override
getSlotIndex(int subscriptionId)651         public int getSlotIndex(int subscriptionId) {
652             return SubscriptionManager.getSlotIndex(subscriptionId);
653         }
654     }
655 
656     @VisibleForTesting
657     static class IconData {
658         public final int subId;
659         public final int slotIndex;
660 
IconData(int subId, int slotIndex)661         IconData(int subId, int slotIndex) {
662             this.subId = subId;
663             this.slotIndex = slotIndex;
664         }
665     }
666 }
667