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