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