1 /*
<lambda>null2 * Copyright (C) 2024 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.statusbar.pipeline.mobile.ui.viewmodel
18
19 import androidx.compose.runtime.State as ComposeState
20 import androidx.compose.runtime.getValue
21 import com.android.systemui.Flags
22 import com.android.systemui.KairosActivatable
23 import com.android.systemui.KairosBuilder
24 import com.android.systemui.activated
25 import com.android.systemui.dagger.SysUISingleton
26 import com.android.systemui.flags.FeatureFlagsClassic
27 import com.android.systemui.kairos.BuildScope
28 import com.android.systemui.kairos.BuildSpec
29 import com.android.systemui.kairos.ExperimentalKairosApi
30 import com.android.systemui.kairos.Incremental
31 import com.android.systemui.kairos.State as KairosState
32 import com.android.systemui.kairos.State
33 import com.android.systemui.kairos.buildSpec
34 import com.android.systemui.kairos.combine
35 import com.android.systemui.kairos.flatten
36 import com.android.systemui.kairos.map
37 import com.android.systemui.kairos.mapValues
38 import com.android.systemui.kairos.stateOf
39 import com.android.systemui.kairosBuilder
40 import com.android.systemui.statusbar.phone.StatusBarLocation
41 import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
42 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos
43 import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos
44 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
45 import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
46 import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
47 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
48 import com.android.systemui.util.composable.kairos.toComposeState
49 import dagger.Provides
50 import dagger.multibindings.ElementsIntoSet
51 import javax.inject.Inject
52 import javax.inject.Provider
53
54 /**
55 * View model for describing the system's current mobile cellular connections. The result is a list
56 * of [MobileIconViewModelKairos]s which describe the individual icons and can be bound to
57 * [ModernStatusBarMobileView].
58 */
59 @ExperimentalKairosApi
60 @SysUISingleton
61 class MobileIconsViewModelKairos
62 @Inject
63 constructor(
64 val logger: MobileViewLogger,
65 private val verboseLogger: VerboseMobileViewLogger,
66 private val interactor: MobileIconsInteractorKairos,
67 private val airplaneModeInteractor: AirplaneModeInteractor,
68 private val constants: ConnectivityConstants,
69 private val flags: FeatureFlagsClassic,
70 ) : KairosBuilder by kairosBuilder() {
71
72 val activeSubscriptionId: State<Int?>
73 get() = interactor.activeDataIconInteractor.map { it?.subscriptionId }
74
75 val subscriptionIds: KairosState<List<Int>> =
76 interactor.filteredSubscriptions.map { subscriptions ->
77 subscriptions.map { it.subscriptionId }
78 }
79
80 val icons: Incremental<Int, MobileIconViewModelKairos> = buildIncremental {
81 interactor.icons
82 .mapValues { (subId, icon) -> buildSpec { commonViewModel(subId, icon) } }
83 .applyLatestSpecForKey()
84 }
85
86 /** Whether the mobile sub that's displayed first visually is showing its network type icon. */
87 val firstMobileSubShowingNetworkTypeIcon: KairosState<Boolean> = buildState {
88 combine(subscriptionIds.map { it.lastOrNull() }, icons) { lastId, icons ->
89 icons[lastId]?.networkTypeIcon?.map { it != null } ?: stateOf(false)
90 }
91 .flatten()
92 }
93
94 val isStackable: KairosState<Boolean>
95 get() = interactor.isStackable
96
97 fun viewModelForSub(
98 subId: Int,
99 location: StatusBarLocation,
100 ): BuildSpec<LocationBasedMobileViewModelKairos> = buildSpec {
101 val iconInteractor =
102 interactor.icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") }
103 val commonViewModel =
104 icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") }
105 LocationBasedMobileViewModelKairos.viewModelForLocation(
106 commonViewModel,
107 iconInteractor,
108 verboseLogger,
109 location,
110 )
111 }
112
113 fun shadeCarrierGroupIcon(subId: Int): BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos> =
114 buildSpec {
115 val iconInteractor =
116 interactor.icons.sample().getOrElse(subId) {
117 error("Unknown subscription id: $subId")
118 }
119 val commonViewModel =
120 icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") }
121 ShadeCarrierGroupMobileIconViewModelKairos(commonViewModel, iconInteractor)
122 }
123
124 private fun BuildScope.commonViewModel(subId: Int, iconInteractor: MobileIconInteractorKairos) =
125 activated {
126 MobileIconViewModelKairos(
127 subscriptionId = subId,
128 iconInteractor = iconInteractor,
129 airplaneModeInteractor = airplaneModeInteractor,
130 constants = constants,
131 flags = flags,
132 )
133 }
134
135 @dagger.Module
136 object Module {
137 @Provides
138 @ElementsIntoSet
139 fun bindKairosActivatable(
140 impl: Provider<MobileIconsViewModelKairos>
141 ): Set<@JvmSuppressWildcards KairosActivatable> =
142 if (Flags.statusBarMobileIconKairos()) setOf(impl.get()) else emptySet()
143 }
144 }
145
146 @ExperimentalKairosApi
147 class MobileIconsViewModelKairosComposeWrapper(
148 icons: ComposeState<Map<Int, MobileIconViewModelKairos>>,
149 val logger: MobileViewLogger,
150 ) {
151 val icons: Map<Int, MobileIconViewModelKairos> by icons
152 }
153
154 @ExperimentalKairosApi
<lambda>null155 fun MobileIconsViewModelKairos.composeWrapper(): BuildSpec<MobileIconsViewModelKairosComposeWrapper> = buildSpec {
156 MobileIconsViewModelKairosComposeWrapper(
157 icons = toComposeState(icons),
158 logger = logger,
159 )
160 }
161