1 /* 2 * 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.airplane.data.repository 18 19 import android.net.ConnectivityManager 20 import android.os.Handler 21 import android.provider.Settings.Global 22 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow 23 import com.android.systemui.dagger.SysUISingleton 24 import com.android.systemui.dagger.qualifiers.Background 25 import com.android.systemui.log.table.TableLogBuffer 26 import com.android.systemui.log.table.logDiffsForTable 27 import com.android.systemui.qs.SettingObserver 28 import com.android.systemui.statusbar.pipeline.dagger.AirplaneTableLog 29 import com.android.systemui.util.settings.GlobalSettings 30 import javax.inject.Inject 31 import kotlin.coroutines.CoroutineContext 32 import kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.channels.awaitClose 34 import kotlinx.coroutines.flow.SharingStarted 35 import kotlinx.coroutines.flow.StateFlow 36 import kotlinx.coroutines.flow.distinctUntilChanged 37 import kotlinx.coroutines.flow.stateIn 38 import kotlinx.coroutines.withContext 39 40 /** 41 * Provides data related to airplane mode. 42 * 43 * IMPORTANT: This is currently *not* used to render any airplane mode information anywhere. It is 44 * only used to help [com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel] 45 * determine what parts of the wifi icon view should be shown. 46 * 47 * TODO(b/238425913): Consider migrating the status bar airplane mode icon to use this repo. 48 */ 49 interface AirplaneModeRepository { 50 /** Observable for whether the device is currently in airplane mode. */ 51 val isAirplaneMode: StateFlow<Boolean> 52 53 /** Sets airplane mode state. */ setIsAirplaneModenull54 suspend fun setIsAirplaneMode(isEnabled: Boolean) 55 } 56 57 @SysUISingleton 58 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") 59 class AirplaneModeRepositoryImpl 60 @Inject 61 constructor( 62 private val connectivityManager: ConnectivityManager, 63 @Background private val bgHandler: Handler?, 64 @Background private val backgroundContext: CoroutineContext, 65 private val globalSettings: GlobalSettings, 66 @AirplaneTableLog logger: TableLogBuffer, 67 @Background scope: CoroutineScope, 68 ) : AirplaneModeRepository { 69 // TODO(b/254848912): Replace this with a generic SettingObserver coroutine once we have it. 70 override val isAirplaneMode: StateFlow<Boolean> = 71 conflatedCallbackFlow { 72 val observer = 73 object : SettingObserver(globalSettings, bgHandler, Global.AIRPLANE_MODE_ON) { 74 override fun handleValueChanged(value: Int, observedChange: Boolean) { 75 trySend(value == 1) 76 } 77 } 78 79 observer.isListening = true 80 trySend(observer.value == 1) 81 awaitClose { observer.isListening = false } 82 } 83 .distinctUntilChanged() 84 .logDiffsForTable(logger, columnName = "isAirplaneMode", initialValue = false) 85 .stateIn( 86 scope, 87 started = SharingStarted.WhileSubscribed(), 88 // When the observer starts listening, the flow will emit the current value so the 89 // initialValue here is irrelevant. 90 initialValue = false, 91 ) 92 93 override suspend fun setIsAirplaneMode(isEnabled: Boolean) = 94 withContext(backgroundContext) { connectivityManager.setAirplaneMode(isEnabled) } 95 } 96