1 /* <lambda>null2 * Copyright (C) 2024 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.data 18 19 import android.os.Bundle 20 import androidx.annotation.VisibleForTesting 21 import com.android.systemui.dagger.SysUISingleton 22 import com.android.systemui.dagger.qualifiers.Application 23 import com.android.systemui.demomode.DemoMode 24 import com.android.systemui.demomode.DemoModeController 25 import com.android.systemui.statusbar.pipeline.satellite.data.demo.DemoDeviceBasedSatelliteRepository 26 import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState 27 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow 28 import javax.inject.Inject 29 import kotlinx.coroutines.CoroutineScope 30 import kotlinx.coroutines.ExperimentalCoroutinesApi 31 import kotlinx.coroutines.channels.awaitClose 32 import kotlinx.coroutines.flow.SharingStarted 33 import kotlinx.coroutines.flow.StateFlow 34 import kotlinx.coroutines.flow.flatMapLatest 35 import kotlinx.coroutines.flow.mapLatest 36 import kotlinx.coroutines.flow.stateIn 37 38 /** 39 * A provider for the [DeviceBasedSatelliteRepository] interface that can choose between the Demo 40 * and Prod concrete implementations at runtime. It works by defining a base flow, [activeRepo], 41 * which switches based on the latest information from [DemoModeController], and switches every flow 42 * in the interface to point to the currently-active provider. This allows us to put the demo mode 43 * interface in its own repository, completely separate from the real version, while still using all 44 * of the prod implementations for the rest of the pipeline (interactors and onward). Looks 45 * something like this: 46 * ``` 47 * RealRepository 48 * │ 49 * ├──►RepositorySwitcher──►RealInteractor──►RealViewModel 50 * │ 51 * DemoRepository 52 * ``` 53 */ 54 @OptIn(ExperimentalCoroutinesApi::class) 55 @SysUISingleton 56 class DeviceBasedSatelliteRepositorySwitcher 57 @Inject 58 constructor( 59 private val realImpl: RealDeviceBasedSatelliteRepository, 60 private val demoImpl: DemoDeviceBasedSatelliteRepository, 61 private val demoModeController: DemoModeController, 62 @Application scope: CoroutineScope, 63 ) : DeviceBasedSatelliteRepository { 64 private val isDemoMode = 65 conflatedCallbackFlow { 66 val callback = 67 object : DemoMode { 68 override fun dispatchDemoCommand(command: String?, args: Bundle?) { 69 // Don't care 70 } 71 72 override fun onDemoModeStarted() { 73 demoImpl.startProcessingCommands() 74 trySend(true) 75 } 76 77 override fun onDemoModeFinished() { 78 demoImpl.stopProcessingCommands() 79 trySend(false) 80 } 81 } 82 83 demoModeController.addCallback(callback) 84 awaitClose { demoModeController.removeCallback(callback) } 85 } 86 .stateIn(scope, SharingStarted.WhileSubscribed(), demoModeController.isInDemoMode) 87 88 @VisibleForTesting 89 val activeRepo: StateFlow<DeviceBasedSatelliteRepository> = 90 isDemoMode 91 .mapLatest { isDemoMode -> 92 if (isDemoMode) { 93 demoImpl 94 } else { 95 realImpl 96 } 97 } 98 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl) 99 100 override val isSatelliteProvisioned: StateFlow<Boolean> = 101 activeRepo 102 .flatMapLatest { it.isSatelliteProvisioned } 103 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isSatelliteProvisioned.value) 104 105 override val connectionState: StateFlow<SatelliteConnectionState> = 106 activeRepo 107 .flatMapLatest { it.connectionState } 108 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.connectionState.value) 109 110 override val signalStrength: StateFlow<Int> = 111 activeRepo 112 .flatMapLatest { it.signalStrength } 113 .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.signalStrength.value) 114 115 override val isSatelliteAllowedForCurrentLocation: StateFlow<Boolean> = 116 activeRepo 117 .flatMapLatest { it.isSatelliteAllowedForCurrentLocation } 118 .stateIn( 119 scope, 120 SharingStarted.WhileSubscribed(), 121 realImpl.isSatelliteAllowedForCurrentLocation.value 122 ) 123 } 124