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