1 /*
<lambda>null2  * Copyright (C) 2022 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.wifi.ui.viewmodel
18 
19 import android.content.Context
20 import com.android.systemui.Flags.statusBarStaticInoutIndicators
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.log.table.TableLogBuffer
24 import com.android.systemui.log.table.logDiffsForTable
25 import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
26 import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON
27 import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
28 import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
29 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
30 import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
31 import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
32 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
33 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
34 import java.util.function.Supplier
35 import javax.inject.Inject
36 import javax.inject.Named
37 import kotlinx.coroutines.CoroutineScope
38 import kotlinx.coroutines.flow.Flow
39 import kotlinx.coroutines.flow.SharingStarted
40 import kotlinx.coroutines.flow.StateFlow
41 import kotlinx.coroutines.flow.combine
42 import kotlinx.coroutines.flow.flowOf
43 import kotlinx.coroutines.flow.map
44 import kotlinx.coroutines.flow.stateIn
45 
46 /**
47  * Models the UI state for the status bar wifi icon.
48  *
49  * This is a singleton so that we don't have duplicate logs and should *not* be used directly to
50  * control views. Instead, use an instance of [LocationBasedWifiViewModel]. See
51  * [LocationBasedWifiViewModel.viewModelForLocation].
52  */
53 @SysUISingleton
54 class WifiViewModel
55 @Inject
56 constructor(
57     airplaneModeViewModel: AirplaneModeViewModel,
58     // TODO(b/238425913): The wifi icon shouldn't need to consume mobile information. A
59     //  container-level view model should do the work instead.
60     @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
61     shouldShowSignalSpacerProvider: Supplier<Flow<Boolean>>,
62     connectivityConstants: ConnectivityConstants,
63     private val context: Context,
64     @WifiTableLog wifiTableLogBuffer: TableLogBuffer,
65     interactor: WifiInteractor,
66     @Application private val scope: CoroutineScope,
67     wifiConstants: WifiConstants,
68 ) : WifiViewModelCommon {
69     override val wifiIcon: StateFlow<WifiIcon> =
70         combine(
71                 interactor.isEnabled,
72                 interactor.isDefault,
73                 interactor.isForceHidden,
74                 interactor.wifiNetwork,
75             ) { isEnabled, isDefault, isForceHidden, wifiNetwork ->
76                 if (!isEnabled || isForceHidden || wifiNetwork is WifiNetworkModel.CarrierMerged) {
77                     return@combine WifiIcon.Hidden
78                 }
79 
80                 // Don't show any hotspot info in the status bar.
81                 val icon = WifiIcon.fromModel(wifiNetwork, context, showHotspotInfo = false)
82 
83                 return@combine when {
84                     isDefault -> icon
85                     wifiConstants.alwaysShowIconIfEnabled -> icon
86                     !connectivityConstants.hasDataCapabilities -> icon
87                     // See b/272509965: Even if we have an active and validated wifi network, we
88                     // don't want to show the icon if wifi isn't the default network.
89                     else -> WifiIcon.Hidden
90                 }
91             }
92             .logDiffsForTable(
93                 wifiTableLogBuffer,
94                 columnPrefix = "",
95                 initialValue = WifiIcon.Hidden,
96             )
97             .stateIn(
98                 scope,
99                 started = SharingStarted.WhileSubscribed(),
100                 initialValue = WifiIcon.Hidden
101             )
102 
103     /** The wifi activity status. Null if we shouldn't display the activity status. */
104     private val activity: Flow<DataActivityModel?> =
105         if (!connectivityConstants.shouldShowActivityConfig) {
106                 flowOf(null)
107             } else {
108                 combine(interactor.activity, interactor.ssid) { activity, ssid ->
109                     when (ssid) {
110                         null -> null
111                         else -> activity
112                     }
113                 }
114             }
115             .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null)
116 
117     override val isActivityInViewVisible: Flow<Boolean> =
118         activity
119             .map { it?.hasActivityIn ?: false }
120             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
121 
122     override val isActivityOutViewVisible: Flow<Boolean> =
123         activity
124             .map { it?.hasActivityOut ?: false }
125             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
126 
127     override val isActivityContainerVisible: Flow<Boolean> =
128         if (statusBarStaticInoutIndicators()) {
129                 flowOf(connectivityConstants.shouldShowActivityConfig)
130             } else {
131                 activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
132             }
133             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
134 
135     // TODO(b/238425913): It isn't ideal for the wifi icon to need to know about whether the
136     //  airplane icon is visible. Instead, we should have a parent StatusBarSystemIconsViewModel
137     //  that appropriately knows about both icons and sets the padding appropriately.
138     override val isAirplaneSpacerVisible: Flow<Boolean> =
139         airplaneModeViewModel.isAirplaneModeIconVisible
140 
141     override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get()
142 }
143