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