1 /*
<lambda>null2  * 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.statusbar.pipeline.satellite.ui.viewmodel
18 
19 import android.content.Context
20 import com.android.systemui.common.shared.model.Icon
21 import com.android.systemui.dagger.SysUISingleton
22 import com.android.systemui.dagger.qualifiers.Application
23 import com.android.systemui.log.LogBuffer
24 import com.android.systemui.log.core.LogLevel
25 import com.android.systemui.res.R
26 import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
27 import com.android.systemui.statusbar.pipeline.dagger.DeviceBasedSatelliteInputLog
28 import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor
29 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState
30 import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel
31 import javax.inject.Inject
32 import kotlin.time.Duration.Companion.seconds
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.ExperimentalCoroutinesApi
35 import kotlinx.coroutines.delay
36 import kotlinx.coroutines.flow.Flow
37 import kotlinx.coroutines.flow.SharingStarted
38 import kotlinx.coroutines.flow.StateFlow
39 import kotlinx.coroutines.flow.combine
40 import kotlinx.coroutines.flow.distinctUntilChanged
41 import kotlinx.coroutines.flow.flatMapLatest
42 import kotlinx.coroutines.flow.flowOf
43 import kotlinx.coroutines.flow.onEach
44 import kotlinx.coroutines.flow.stateIn
45 
46 /**
47  * View-Model for the device-based satellite icon. This icon will only show in the status bar if
48  * satellite is available AND all other service states are considered OOS.
49  */
50 interface DeviceBasedSatelliteViewModel {
51     /**
52      * The satellite icon that should be displayed, or null if no satellite icon should be
53      * displayed.
54      */
55     val icon: StateFlow<Icon?>
56 
57     /**
58      * The satellite-related text that should be used as the carrier text string when satellite is
59      * active, or null if the carrier text string shouldn't include any satellite information.
60      */
61     val carrierText: StateFlow<String?>
62 }
63 
64 @OptIn(ExperimentalCoroutinesApi::class)
65 @SysUISingleton
66 class DeviceBasedSatelliteViewModelImpl
67 @Inject
68 constructor(
69     context: Context,
70     interactor: DeviceBasedSatelliteInteractor,
71     @Application scope: CoroutineScope,
72     airplaneModeRepository: AirplaneModeRepository,
73     @DeviceBasedSatelliteInputLog logBuffer: LogBuffer,
74 ) : DeviceBasedSatelliteViewModel {
75     private val shouldShowIcon: Flow<Boolean> =
allOosnull76         interactor.areAllConnectionsOutOfService.flatMapLatest { allOos ->
77             if (!allOos) {
78                 flowOf(false)
79             } else {
80                 combine(
81                     interactor.isSatelliteAllowed,
82                     interactor.isSatelliteProvisioned,
83                     interactor.isWifiActive,
84                     airplaneModeRepository.isAirplaneMode
85                 ) { isSatelliteAllowed, isSatelliteProvisioned, isWifiActive, isAirplaneMode ->
86                     isSatelliteAllowed && isSatelliteProvisioned && !isWifiActive && !isAirplaneMode
87                 }
88             }
89         }
90 
91     // This adds a 10 seconds delay before showing the icon
92     private val shouldActuallyShowIcon: StateFlow<Boolean> =
93         shouldShowIcon
94             .distinctUntilChanged()
shouldShownull95             .flatMapLatest { shouldShow ->
96                 if (shouldShow) {
97                     logBuffer.log(
98                         TAG,
99                         LogLevel.INFO,
100                         { long1 = DELAY_DURATION.inWholeSeconds },
101                         { "Waiting $long1 seconds before showing the satellite icon" }
102                     )
103                     delay(DELAY_DURATION)
104                     flowOf(true)
105                 } else {
106                     flowOf(false)
107                 }
108             }
109             .stateIn(scope, SharingStarted.WhileSubscribed(), false)
110 
111     override val icon: StateFlow<Icon?> =
112         combine(
113                 shouldActuallyShowIcon,
114                 interactor.connectionState,
115                 interactor.signalStrength,
signalStrengthnull116             ) { shouldShow, state, signalStrength ->
117                 if (shouldShow) {
118                     SatelliteIconModel.fromConnectionState(state, signalStrength)
119                 } else {
120                     null
121                 }
122             }
123             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
124 
125     override val carrierText: StateFlow<String?> =
126         combine(
127                 shouldActuallyShowIcon,
128                 interactor.connectionState,
connectionStatenull129             ) { shouldShow, connectionState ->
130                 logBuffer.log(
131                     TAG,
132                     LogLevel.INFO,
133                     {
134                         bool1 = shouldShow
135                         str1 = connectionState.name
136                     },
137                     { "Updating carrier text. shouldActuallyShow=$bool1 connectionState=$str1" }
138                 )
139                 if (shouldShow) {
140                     when (connectionState) {
141                         SatelliteConnectionState.On,
142                         SatelliteConnectionState.Connected ->
143                             context.getString(R.string.satellite_connected_carrier_text)
144                         SatelliteConnectionState.Off,
145                         SatelliteConnectionState.Unknown -> {
146                             null
147                         }
148                     }
149                 } else {
150                     null
151                 }
152             }
<lambda>null153             .onEach {
154                 logBuffer.log(
155                     TAG,
156                     LogLevel.INFO,
157                     { str1 = it },
158                     { "Resulting carrier text = $str1" }
159                 )
160             }
161             .stateIn(scope, SharingStarted.WhileSubscribed(), null)
162 
163     companion object {
164         private const val TAG = "DeviceBasedSatelliteViewModel"
165         private val DELAY_DURATION = 10.seconds
166     }
167 }
168