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.wifi.data.repository 18 19 import android.os.Bundle 20 import androidx.annotation.VisibleForTesting 21 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 22 import com.android.systemui.dagger.SysUISingleton 23 import com.android.systemui.dagger.qualifiers.Application 24 import com.android.systemui.demomode.DemoMode 25 import com.android.systemui.demomode.DemoModeController 26 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel 27 import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository 28 import com.android.systemui.statusbar.pipeline.wifi.data.repository.prod.WifiRepositoryImpl 29 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel 30 import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiScanEntry 31 import javax.inject.Inject 32 import kotlinx.coroutines.CoroutineScope 33 import kotlinx.coroutines.ExperimentalCoroutinesApi 34 import kotlinx.coroutines.channels.awaitClose 35 import kotlinx.coroutines.flow.SharingStarted 36 import kotlinx.coroutines.flow.StateFlow 37 import kotlinx.coroutines.flow.flatMapLatest 38 import kotlinx.coroutines.flow.mapLatest 39 import kotlinx.coroutines.flow.stateIn 40 41 /** 42 * Provides the [WifiRepository] interface either through the [DemoWifiRepository] implementation, 43 * or the [WifiRepositoryImpl]'s prod implementation, based on the current demo mode value. In this 44 * way, downstream clients can all consist of real implementations and not care about which 45 * repository is responsible for the data. Graphically: 46 * ``` 47 * RealRepository 48 * │ 49 * ├──►RepositorySwitcher──►RealInteractor──►RealViewModel 50 * │ 51 * DemoRepository 52 * ``` 53 * 54 * When demo mode turns on, every flow will [flatMapLatest] to the current provider's version of 55 * that flow. 56 */ 57 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") 58 @OptIn(ExperimentalCoroutinesApi::class) 59 @SysUISingleton 60 class WifiRepositorySwitcher 61 @Inject 62 constructor( 63 private val realImpl: RealWifiRepository, 64 private val demoImpl: DemoWifiRepository, 65 private val demoModeController: DemoModeController, 66 @Application scope: CoroutineScope, 67 ) : WifiRepository { 68 private val isDemoMode = 69 conflatedCallbackFlow { 70 val callback = 71 object : DemoMode { 72 override fun dispatchDemoCommand(command: String?, args: Bundle?) { 73 // Don't care 74 } 75 76 override fun onDemoModeStarted() { 77 demoImpl.startProcessingCommands() 78 trySend(true) 79 } 80 81 override fun onDemoModeFinished() { 82 demoImpl.stopProcessingCommands() 83 trySend(false) 84 } 85 } 86 87 demoModeController.addCallback(callback) 88 awaitClose { demoModeController.removeCallback(callback) } 89 } 90 .stateIn(scope, SharingStarted.WhileSubscribed(), demoModeController.isInDemoMode) 91 92 @VisibleForTesting 93 val activeRepo = 94 isDemoMode 95 .mapLatest { isDemoMode -> 96 if (isDemoMode) { 97 demoImpl 98 } else { 99 realImpl 100 } 101 } 102 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl) 103 104 override val isWifiEnabled: StateFlow<Boolean> = 105 activeRepo 106 .flatMapLatest { it.isWifiEnabled } 107 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isWifiEnabled.value) 108 109 override val isWifiDefault: StateFlow<Boolean> = 110 activeRepo 111 .flatMapLatest { it.isWifiDefault } 112 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isWifiDefault.value) 113 114 override val wifiNetwork: StateFlow<WifiNetworkModel> = 115 activeRepo 116 .flatMapLatest { it.wifiNetwork } 117 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiNetwork.value) 118 119 override val secondaryNetworks: StateFlow<List<WifiNetworkModel>> = 120 activeRepo 121 .flatMapLatest { it.secondaryNetworks } 122 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.secondaryNetworks.value) 123 124 override val wifiActivity: StateFlow<DataActivityModel> = 125 activeRepo 126 .flatMapLatest { it.wifiActivity } 127 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiActivity.value) 128 129 override val wifiScanResults: StateFlow<List<WifiScanEntry>> = 130 activeRepo 131 .flatMapLatest { it.wifiScanResults } 132 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiScanResults.value) 133 } 134