diff options
9 files changed, 523 insertions, 7 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java index 57add75ee38a..5f5418f5eeb9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java @@ -1302,7 +1302,7 @@ public class NetworkControllerImpl extends BroadcastReceiver } } String wifi = args.getString("wifi"); - if (wifi != null) { + if (wifi != null && !mStatusBarPipelineFlags.runNewWifiIconBackend()) { boolean show = wifi.equals("show"); String level = args.getString("level"); if (level != null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 5569718f4a4a..c72eb054c62c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -42,6 +42,8 @@ import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconStat import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; +import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView; +import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel; import java.util.ArrayList; import java.util.List; @@ -56,6 +58,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private final int mIconSize; private StatusBarWifiView mWifiView; + private ModernStatusBarWifiView mModernWifiView; private boolean mDemoMode; private int mColor; @@ -236,14 +239,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void addDemoWifiView(WifiIconState state) { Log.d(TAG, "addDemoWifiView: "); - // TODO(b/238425913): Migrate this view to {@code ModernStatusBarWifiView}. StatusBarWifiView view = StatusBarWifiView.fromContext(mContext, state.slot); int viewIndex = getChildCount(); // If we have mobile views, put wifi before them for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); - if (child instanceof StatusBarMobileView) { + if (child instanceof StatusBarMobileView + || child instanceof ModernStatusBarMobileView) { viewIndex = i; break; } @@ -299,6 +302,30 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } /** + * Add a {@link ModernStatusBarWifiView} + */ + public void addModernWifiView(LocationBasedWifiViewModel viewModel) { + Log.d(TAG, "addModernDemoWifiView: "); + ModernStatusBarWifiView view = ModernStatusBarWifiView + .constructAndBind(mContext, "wifi", viewModel); + + int viewIndex = getChildCount(); + // If we have mobile views, put wifi before them + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof StatusBarMobileView + || child instanceof ModernStatusBarMobileView) { + viewIndex = i; + break; + } + } + + mModernWifiView = view; + mModernWifiView.setStaticDrawableColor(mColor); + addView(view, viewIndex, createLayoutParams()); + } + + /** * Apply an update to a mobile icon view for the given {@link MobileIconState}. For * compatibility with {@link MobileContextProvider}, we have to recreate the view every time we * update it, since the context (and thus the {@link Configuration}) may have changed @@ -320,8 +347,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da public void onRemoveIcon(StatusIconDisplayable view) { if (view.getSlot().equals("wifi")) { - removeView(mWifiView); - mWifiView = null; + if (view instanceof StatusBarWifiView) { + removeView(mWifiView); + mWifiView = null; + } else if (view instanceof ModernStatusBarWifiView) { + Log.d(TAG, "onRemoveIcon: removing modern wifi view"); + removeView(mModernWifiView); + mModernWifiView = null; + } } else if (view instanceof StatusBarMobileView) { StatusBarMobileView mobileView = matchingMobileView(view); if (mobileView != null) { @@ -374,8 +407,14 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da if (mWifiView != null) { mWifiView.onDarkChanged(areas, darkIntensity, tint); } + if (mModernWifiView != null) { + mModernWifiView.onDarkChanged(areas, darkIntensity, tint); + } for (StatusBarMobileView view : mMobileViews) { view.onDarkChanged(areas, darkIntensity, tint); } + for (ModernStatusBarMobileView view : mModernMobileViews) { + view.onDarkChanged(areas, darkIntensity, tint); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 44e5cd9543ef..1a14a0363763 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -497,6 +497,11 @@ public interface StatusBarIconController { ModernStatusBarWifiView view = onCreateModernStatusBarWifiView(slot); mGroup.addView(view, index, onCreateLayoutParams()); + + if (mIsInDemoMode) { + mDemoStatusIcons.addModernWifiView(mWifiViewModel); + } + return view; } @@ -688,6 +693,9 @@ public interface StatusBarIconController { mIsInDemoMode = true; if (mDemoStatusIcons == null) { mDemoStatusIcons = createDemoStatusIcons(); + if (mStatusBarPipelineFlags.useNewWifiIcon()) { + mDemoStatusIcons.addModernWifiView(mWifiViewModel); + } } mDemoStatusIcons.onDemoModeStarted(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index c350c78913d3..0d01715715c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyIm import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository -import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl +import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl import dagger.Binds @@ -56,7 +56,7 @@ abstract class StatusBarPipelineModule { @Binds abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository - @Binds abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository + @Binds abstract fun wifiRepository(impl: WifiRepositorySwitcher): WifiRepository @Binds abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt new file mode 100644 index 000000000000..73bcdfd2b78e --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcher.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.wifi.data.repository + +import android.os.Bundle +import androidx.annotation.VisibleForTesting +import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.demomode.DemoMode +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel +import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.stateIn + +/** + * Provides the [WifiRepository] interface either through the [DemoWifiRepository] implementation, + * or the [WifiRepositoryImpl]'s prod implementation, based on the current demo mode value. In this + * way, downstream clients can all consist of real implementations and not care about which + * repository is responsible for the data. Graphically: + * + * ``` + * RealRepository + * │ + * ├──►RepositorySwitcher──►RealInteractor──►RealViewModel + * │ + * DemoRepository + * ``` + * + * When demo mode turns on, every flow will [flatMapLatest] to the current provider's version of + * that flow. + */ +@Suppress("EXPERIMENTAL_IS_NOT_ENABLED") +@OptIn(ExperimentalCoroutinesApi::class) +class WifiRepositorySwitcher +@Inject +constructor( + private val realImpl: WifiRepositoryImpl, + private val demoImpl: DemoWifiRepository, + private val demoModeController: DemoModeController, + @Application scope: CoroutineScope, +) : WifiRepository { + private val isDemoMode = + conflatedCallbackFlow { + val callback = + object : DemoMode { + override fun dispatchDemoCommand(command: String?, args: Bundle?) { + // Don't care + } + + override fun onDemoModeStarted() { + demoImpl.startProcessingCommands() + trySend(true) + } + + override fun onDemoModeFinished() { + demoImpl.stopProcessingCommands() + trySend(false) + } + } + + demoModeController.addCallback(callback) + awaitClose { demoModeController.removeCallback(callback) } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), demoModeController.isInDemoMode) + + @VisibleForTesting + val activeRepo = + isDemoMode + .mapLatest { isDemoMode -> + if (isDemoMode) { + demoImpl + } else { + realImpl + } + } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl) + + override val isWifiEnabled: StateFlow<Boolean> = + activeRepo + .flatMapLatest { it.isWifiEnabled } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isWifiEnabled.value) + + override val isWifiDefault: StateFlow<Boolean> = + activeRepo + .flatMapLatest { it.isWifiDefault } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.isWifiDefault.value) + + override val wifiNetwork: StateFlow<WifiNetworkModel> = + activeRepo + .flatMapLatest { it.wifiNetwork } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiNetwork.value) + + override val wifiActivity: StateFlow<DataActivityModel> = + activeRepo + .flatMapLatest { it.wifiActivity } + .stateIn(scope, SharingStarted.WhileSubscribed(), realImpl.wifiActivity.value) +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt new file mode 100644 index 000000000000..c588945fbd67 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoModeWifiDataSource.kt @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.wifi.data.repository.demo + +import android.net.wifi.WifiManager +import android.os.Bundle +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.shareIn + +/** Data source to map between demo mode commands and inputs into [DemoWifiRepository]'s flows */ +@SysUISingleton +class DemoModeWifiDataSource +@Inject +constructor( + demoModeController: DemoModeController, + @Application scope: CoroutineScope, +) { + private val demoCommandStream = demoModeController.demoFlowForCommand(COMMAND_NETWORK) + private val _wifiCommands = demoCommandStream.map { args -> args.toWifiEvent() } + val wifiEvents = _wifiCommands.shareIn(scope, SharingStarted.WhileSubscribed()) + + private fun Bundle.toWifiEvent(): FakeWifiEventModel? { + val wifi = getString("wifi") ?: return null + return if (wifi == "show") { + activeWifiEvent() + } else { + FakeWifiEventModel.WifiDisabled + } + } + + private fun Bundle.activeWifiEvent(): FakeWifiEventModel.Wifi { + val level = getString("level")?.toInt() + val activity = getString("activity")?.toActivity() + val ssid = getString("ssid") + val validated = getString("fully").toBoolean() + + return FakeWifiEventModel.Wifi( + level = level, + activity = activity, + ssid = ssid, + validated = validated, + ) + } + + private fun String.toActivity(): Int = + when (this) { + "inout" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT + "in" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN + "out" -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT + else -> WifiManager.TrafficStateCallback.DATA_ACTIVITY_NONE + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt new file mode 100644 index 000000000000..7890074cf8a2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.wifi.data.repository.demo + +import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel +import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel +import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel +import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.launch + +/** Demo-able wifi repository to support SystemUI demo mode commands. */ +class DemoWifiRepository +@Inject +constructor( + private val dataSource: DemoModeWifiDataSource, + @Application private val scope: CoroutineScope, +) : WifiRepository { + private var demoCommandJob: Job? = null + + private val _isWifiEnabled = MutableStateFlow(false) + override val isWifiEnabled: StateFlow<Boolean> = _isWifiEnabled + + private val _isWifiDefault = MutableStateFlow(false) + override val isWifiDefault: StateFlow<Boolean> = _isWifiDefault + + private val _wifiNetwork = MutableStateFlow<WifiNetworkModel>(WifiNetworkModel.Inactive) + override val wifiNetwork: StateFlow<WifiNetworkModel> = _wifiNetwork + + private val _wifiActivity = + MutableStateFlow(DataActivityModel(hasActivityIn = false, hasActivityOut = false)) + override val wifiActivity: StateFlow<DataActivityModel> = _wifiActivity + + fun startProcessingCommands() { + demoCommandJob = + scope.launch { + dataSource.wifiEvents.filterNotNull().collect { event -> processEvent(event) } + } + } + + fun stopProcessingCommands() { + demoCommandJob?.cancel() + } + + private fun processEvent(event: FakeWifiEventModel) = + when (event) { + is FakeWifiEventModel.Wifi -> processEnabledWifiState(event) + is FakeWifiEventModel.WifiDisabled -> processDisabledWifiState() + } + + private fun processDisabledWifiState() { + _isWifiEnabled.value = false + _isWifiDefault.value = false + _wifiActivity.value = DataActivityModel(hasActivityIn = false, hasActivityOut = false) + _wifiNetwork.value = WifiNetworkModel.Inactive + } + + private fun processEnabledWifiState(event: FakeWifiEventModel.Wifi) { + _isWifiEnabled.value = true + _isWifiDefault.value = true + _wifiActivity.value = + event.activity?.toWifiDataActivityModel() + ?: DataActivityModel(hasActivityIn = false, hasActivityOut = false) + _wifiNetwork.value = event.toWifiNetworkModel() + } + + private fun FakeWifiEventModel.Wifi.toWifiNetworkModel(): WifiNetworkModel = + WifiNetworkModel.Active( + networkId = DEMO_NET_ID, + isValidated = validated ?: true, + level = level, + ssid = ssid, + + // These fields below aren't supported in demo mode, since they aren't needed to satisfy + // the interface. + isPasspointAccessPoint = false, + isOnlineSignUpForPasspointAccessPoint = false, + passpointProviderFriendlyName = null, + ) + + companion object { + private const val DEMO_NET_ID = 1234 + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt new file mode 100644 index 000000000000..2353fb82f3b1 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/model/FakeWifiEventModel.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model + +/** + * Model for demo wifi commands, ported from [NetworkControllerImpl] + * + * Nullable fields represent optional command line arguments + */ +sealed interface FakeWifiEventModel { + data class Wifi( + val level: Int?, + val activity: Int?, + val ssid: String?, + val validated: Boolean?, + ) : FakeWifiEventModel + + object WifiDisabled : FakeWifiEventModel +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt new file mode 100644 index 000000000000..b935442fd73a --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositorySwitcherTest.kt @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.wifi.data.repository + +import android.net.ConnectivityManager +import android.net.wifi.WifiManager +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.demomode.DemoMode +import com.android.systemui.demomode.DemoModeController +import com.android.systemui.log.table.TableLogBuffer +import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger +import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoModeWifiDataSource +import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.DemoWifiRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.demo.model.FakeWifiEventModel +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.kotlinArgumentCaptor +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +class WifiRepositorySwitcherTest : SysuiTestCase() { + private lateinit var underTest: WifiRepositorySwitcher + private lateinit var realImpl: WifiRepositoryImpl + private lateinit var demoImpl: DemoWifiRepository + + @Mock private lateinit var demoModeController: DemoModeController + @Mock private lateinit var logger: ConnectivityPipelineLogger + @Mock private lateinit var tableLogger: TableLogBuffer + @Mock private lateinit var connectivityManager: ConnectivityManager + @Mock private lateinit var wifiManager: WifiManager + @Mock private lateinit var demoModeWifiDataSource: DemoModeWifiDataSource + private val demoModelFlow = MutableStateFlow<FakeWifiEventModel?>(null) + + private val mainExecutor = FakeExecutor(FakeSystemClock()) + + private val testDispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(testDispatcher) + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + + // Never start in demo mode + whenever(demoModeController.isInDemoMode).thenReturn(false) + + realImpl = + WifiRepositoryImpl( + fakeBroadcastDispatcher, + connectivityManager, + logger, + tableLogger, + mainExecutor, + testScope.backgroundScope, + wifiManager, + ) + + whenever(demoModeWifiDataSource.wifiEvents).thenReturn(demoModelFlow) + + demoImpl = + DemoWifiRepository( + demoModeWifiDataSource, + testScope.backgroundScope, + ) + + underTest = + WifiRepositorySwitcher( + realImpl, + demoImpl, + demoModeController, + testScope.backgroundScope, + ) + } + + @Test + fun `switcher active repo - updates when demo mode changes`() = + testScope.runTest { + assertThat(underTest.activeRepo.value).isSameInstanceAs(realImpl) + + var latest: WifiRepository? = null + val job = underTest.activeRepo.onEach { latest = it }.launchIn(this) + + startDemoMode() + + assertThat(latest).isSameInstanceAs(demoImpl) + + finishDemoMode() + + assertThat(latest).isSameInstanceAs(realImpl) + + job.cancel() + } + + private fun startDemoMode() { + whenever(demoModeController.isInDemoMode).thenReturn(true) + getDemoModeCallback().onDemoModeStarted() + } + + private fun finishDemoMode() { + whenever(demoModeController.isInDemoMode).thenReturn(false) + getDemoModeCallback().onDemoModeFinished() + } + + private fun getDemoModeCallback(): DemoMode { + val captor = kotlinArgumentCaptor<DemoMode>() + Mockito.verify(demoModeController).addCallback(captor.capture()) + return captor.value + } +} |