diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java index b5859616f3927..ccaab1adaf266 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeaderController.java @@ -30,6 +30,7 @@ import com.android.systemui.qs.carrier.QSCarrierGroupController; import com.android.systemui.qs.dagger.QSScope; import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider; import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.StatusIconContainer; import com.android.systemui.statusbar.policy.Clock; import com.android.systemui.statusbar.policy.VariableDateViewController; @@ -104,7 +105,7 @@ class QuickStatusBarHeaderController extends ViewController { diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt index fe40d4cbe23a3..d3ed47407b9d2 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt @@ -48,6 +48,7 @@ import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QQS import com.android.systemui.shade.LargeScreenShadeHeaderController.Companion.QS_HEADER_CONSTRAINT import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider import com.android.systemui.statusbar.phone.StatusBarIconController +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.LARGE_SCREEN_BATTERY_CONTROLLER @@ -261,7 +262,7 @@ class LargeScreenShadeHeaderController @Inject constructor( batteryMeterViewController.ignoreTunerUpdates() batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE) - iconManager = tintedIconManagerFactory.create(iconContainer) + iconManager = tintedIconManagerFactory.create(iconContainer, StatusBarLocation.QS) iconManager.setTint( Utils.getColorAttrDefaultColor(header.context, android.R.attr.textColorPrimary) ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index ce2c9c2446962..0026b71a53049 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -352,8 +352,8 @@ public class KeyguardStatusBarViewController extends ViewController wifiViewModelProvider, + WifiViewModel wifiViewModel, MobileContextProvider mobileContextProvider, DarkIconDispatcher darkIconDispatcher) { super(linearLayout, + location, statusBarPipelineFlags, - wifiViewModelProvider, + wifiViewModel, mobileContextProvider); mIconHPadding = mContext.getResources().getDimensionPixelSize( R.dimen.status_bar_icon_padding); @@ -204,27 +205,28 @@ public interface StatusBarIconController { @SysUISingleton public static class Factory { private final StatusBarPipelineFlags mStatusBarPipelineFlags; - private final Provider mWifiViewModelProvider; + private final WifiViewModel mWifiViewModel; private final MobileContextProvider mMobileContextProvider; private final DarkIconDispatcher mDarkIconDispatcher; @Inject public Factory( StatusBarPipelineFlags statusBarPipelineFlags, - Provider wifiViewModelProvider, + WifiViewModel wifiViewModel, MobileContextProvider mobileContextProvider, DarkIconDispatcher darkIconDispatcher) { mStatusBarPipelineFlags = statusBarPipelineFlags; - mWifiViewModelProvider = wifiViewModelProvider; + mWifiViewModel = wifiViewModel; mMobileContextProvider = mobileContextProvider; mDarkIconDispatcher = darkIconDispatcher; } - public DarkIconManager create(LinearLayout group) { + public DarkIconManager create(LinearLayout group, StatusBarLocation location) { return new DarkIconManager( group, + location, mStatusBarPipelineFlags, - mWifiViewModelProvider, + mWifiViewModel, mMobileContextProvider, mDarkIconDispatcher); } @@ -239,12 +241,14 @@ public interface StatusBarIconController { public TintedIconManager( ViewGroup group, + StatusBarLocation location, StatusBarPipelineFlags statusBarPipelineFlags, - Provider wifiViewModelProvider, + WifiViewModel wifiViewModel, MobileContextProvider mobileContextProvider) { super(group, + location, statusBarPipelineFlags, - wifiViewModelProvider, + wifiViewModel, mobileContextProvider); } @@ -278,24 +282,25 @@ public interface StatusBarIconController { @SysUISingleton public static class Factory { private final StatusBarPipelineFlags mStatusBarPipelineFlags; - private final Provider mWifiViewModelProvider; + private final WifiViewModel mWifiViewModel; private final MobileContextProvider mMobileContextProvider; @Inject public Factory( StatusBarPipelineFlags statusBarPipelineFlags, - Provider wifiViewModelProvider, + WifiViewModel wifiViewModel, MobileContextProvider mobileContextProvider) { mStatusBarPipelineFlags = statusBarPipelineFlags; - mWifiViewModelProvider = wifiViewModelProvider; + mWifiViewModel = wifiViewModel; mMobileContextProvider = mobileContextProvider; } - public TintedIconManager create(ViewGroup group) { + public TintedIconManager create(ViewGroup group, StatusBarLocation location) { return new TintedIconManager( group, + location, mStatusBarPipelineFlags, - mWifiViewModelProvider, + mWifiViewModel, mMobileContextProvider); } } @@ -306,8 +311,9 @@ public interface StatusBarIconController { */ class IconManager implements DemoModeCommandReceiver { protected final ViewGroup mGroup; + private final StatusBarLocation mLocation; private final StatusBarPipelineFlags mStatusBarPipelineFlags; - private final Provider mWifiViewModelProvider; + private final WifiViewModel mWifiViewModel; private final MobileContextProvider mMobileContextProvider; protected final Context mContext; protected final int mIconSize; @@ -324,12 +330,14 @@ public interface StatusBarIconController { public IconManager( ViewGroup group, + StatusBarLocation location, StatusBarPipelineFlags statusBarPipelineFlags, - Provider wifiViewModelProvider, + WifiViewModel wifiViewModel, MobileContextProvider mobileContextProvider) { mGroup = group; + mLocation = location; mStatusBarPipelineFlags = statusBarPipelineFlags; - mWifiViewModelProvider = wifiViewModelProvider; + mWifiViewModel = wifiViewModel; mMobileContextProvider = mobileContextProvider; mContext = group.getContext(); mIconSize = mContext.getResources().getDimensionPixelSize( @@ -446,7 +454,7 @@ public interface StatusBarIconController { private ModernStatusBarWifiView onCreateModernStatusBarWifiView(String slot) { return ModernStatusBarWifiView.constructAndBind( - mContext, slot, mWifiViewModelProvider.get()); + mContext, slot, mWifiViewModel, mLocation); } private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt new file mode 100644 index 0000000000000..5ace22695ec37 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLocation.kt @@ -0,0 +1,27 @@ +/* + * 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.phone + +/** An enumeration of the different locations that host a status bar. */ +enum class StatusBarLocation { + /** Home screen or in-app. */ + HOME, + /** Keyguard (aka lockscreen). */ + KEYGUARD, + /** Quick settings (inside the shade). */ + QS, +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java index ce04fb5999635..e1215ee952380 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java @@ -68,6 +68,7 @@ import com.android.systemui.statusbar.phone.PhoneStatusBarView; import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; +import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable; @@ -250,7 +251,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue mStatusBar.restoreHierarchyState( savedInstanceState.getSparseParcelableArray(EXTRA_PANEL_STATE)); } - mDarkIconManager = mDarkIconManagerFactory.create(view.findViewById(R.id.statusIcons)); + mDarkIconManager = mDarkIconManagerFactory.create( + view.findViewById(R.id.statusIcons), StatusBarLocation.HOME); mDarkIconManager.setShouldLog(true); updateBlockedIcons(); mStatusBarIconController.addIconGroup(mDarkIconManager); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt index 26667ab6d413c..c3a9b90c5a627 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt @@ -26,6 +26,8 @@ import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.R import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.phone.StatusBarLocation +import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.collect @@ -41,11 +43,29 @@ import kotlinx.coroutines.launch */ @OptIn(InternalCoroutinesApi::class) object WifiViewBinder { - /** Binds the view to the view-model, continuing to update the former based on the latter. */ + + /** + * Binds the view to the appropriate view-model based on the given location. The view will + * continue to be updated following updates from the view-model. + */ @JvmStatic fun bind( view: ViewGroup, - viewModel: WifiViewModel, + wifiViewModel: WifiViewModel, + location: StatusBarLocation, + ) { + when (location) { + StatusBarLocation.HOME -> bind(view, wifiViewModel.home) + StatusBarLocation.KEYGUARD -> bind(view, wifiViewModel.keyguard) + StatusBarLocation.QS -> bind(view, wifiViewModel.qs) + } + } + + /** Binds the view to the view-model, continuing to update the former based on the latter. */ + @JvmStatic + private fun bind( + view: ViewGroup, + viewModel: LocationBasedWifiViewModel, ) { val iconView = view.requireViewById(R.id.wifi_signal) val activityInView = view.requireViewById(R.id.wifi_in) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt index c14a897fffab3..f225b65179ed1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiView.kt @@ -23,6 +23,7 @@ import android.view.LayoutInflater import com.android.systemui.R import com.android.systemui.statusbar.BaseStatusBarWifiView import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.wifi.ui.binder.WifiViewBinder import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel @@ -72,21 +73,22 @@ class ModernStatusBarWifiView( companion object { /** - * Inflates a new instance of [ModernStatusBarWifiView], binds it to [viewModel], and + * Inflates a new instance of [ModernStatusBarWifiView], binds it to a view model, and * returns it. */ @JvmStatic fun constructAndBind( context: Context, slot: String, - viewModel: WifiViewModel, + wifiViewModel: WifiViewModel, + location: StatusBarLocation, ): ModernStatusBarWifiView { return ( LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null) as ModernStatusBarWifiView ).also { it.setSlot(slot) - WifiViewBinder.bind(it, viewModel) + WifiViewBinder.bind(it, wifiViewModel, location) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt new file mode 100644 index 0000000000000..0847e6214337f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/HomeWifiViewModel.kt @@ -0,0 +1,42 @@ +/* + * 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.ui.viewmodel + +import android.graphics.Color +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import kotlinx.coroutines.flow.Flow + +/** + * A view model for the wifi icon shown on the "home" page (aka, when the device is unlocked and not + * showing the shade, so the user is on the home-screen, or in an app). + */ +class HomeWifiViewModel( + statusBarPipelineFlags: StatusBarPipelineFlags, + wifiIcon: Flow, + isActivityInViewVisible: Flow, + isActivityOutViewVisible: Flow, + isActivityContainerVisible: Flow, +) : + LocationBasedWifiViewModel( + statusBarPipelineFlags, + debugTint = Color.CYAN, + wifiIcon, + isActivityInViewVisible, + isActivityOutViewVisible, + isActivityContainerVisible, + ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt new file mode 100644 index 0000000000000..3f7c8e13d7f72 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/KeyguardWifiViewModel.kt @@ -0,0 +1,39 @@ +/* + * 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.ui.viewmodel + +import android.graphics.Color +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import kotlinx.coroutines.flow.Flow + +/** A view model for the wifi icon shown on keyguard (lockscreen). */ +class KeyguardWifiViewModel( + statusBarPipelineFlags: StatusBarPipelineFlags, + wifiIcon: Flow, + isActivityInViewVisible: Flow, + isActivityOutViewVisible: Flow, + isActivityContainerVisible: Flow, +) : + LocationBasedWifiViewModel( + statusBarPipelineFlags, + debugTint = Color.MAGENTA, + wifiIcon, + isActivityInViewVisible, + isActivityOutViewVisible, + isActivityContainerVisible, + ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt new file mode 100644 index 0000000000000..d34ba88cde1a8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/LocationBasedWifiViewModel.kt @@ -0,0 +1,67 @@ +/* + * 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.ui.viewmodel + +import android.graphics.Color +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +/** + * A view model for a wifi icon in a specific location. This allows us to control parameters that + * are location-specific (for example, different tints of the icon in different locations). + * + * Must be subclassed for each distinct location. + */ +abstract class LocationBasedWifiViewModel( + statusBarPipelineFlags: StatusBarPipelineFlags, + debugTint: Int, + + /** The wifi icon that should be displayed. Null if we shouldn't display any icon. */ + val wifiIcon: Flow, + + /** True if the activity in view should be visible. */ + val isActivityInViewVisible: Flow, + + /** True if the activity out view should be visible. */ + val isActivityOutViewVisible: Flow, + + /** True if the activity container view should be visible. */ + val isActivityContainerVisible: Flow, +) { + /** The color that should be used to tint the icon. */ + val tint: Flow = + flowOf( + if (statusBarPipelineFlags.useNewPipelineDebugColoring()) { + debugTint + } else { + DEFAULT_TINT + } + ) + + companion object { + /** + * A default icon tint. + * + * TODO(b/238425913): The tint is actually controlled by + * [com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager]. We + * should use that logic instead of white as a default. + */ + private const val DEFAULT_TINT = Color.WHITE + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt new file mode 100644 index 0000000000000..8f6c26a6620fa --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/QsWifiViewModel.kt @@ -0,0 +1,39 @@ +/* + * 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.ui.viewmodel + +import android.graphics.Color +import com.android.systemui.common.shared.model.Icon +import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import kotlinx.coroutines.flow.Flow + +/** A view model for the wifi icon shown in quick settings (when the shade is pulled down). */ +class QsWifiViewModel( + statusBarPipelineFlags: StatusBarPipelineFlags, + wifiIcon: Flow, + isActivityInViewVisible: Flow, + isActivityOutViewVisible: Flow, + isActivityContainerVisible: Flow, +) : + LocationBasedWifiViewModel( + statusBarPipelineFlags, + debugTint = Color.GREEN, + wifiIcon, + isActivityInViewVisible, + isActivityOutViewVisible, + isActivityContainerVisible, + ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt index 8197e89cb93ef..dae701436be29 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt @@ -17,7 +17,6 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel import android.content.Context -import android.graphics.Color import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting @@ -26,6 +25,8 @@ import com.android.settingslib.AccessibilityContentDescriptions.WIFI_NO_CONNECTI import com.android.systemui.R import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK @@ -37,68 +38,90 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn /** * Models the UI state for the status bar wifi icon. + * + * This class exposes three view models, one per status bar location: + * - [home] + * - [keyguard] + * - [qs] + * In order to get the UI state for the wifi icon, you must use one of those view models (whichever + * is correct for your location). + * + * Internally, this class maintains the current state of the wifi icon and notifies those three + * view models of any changes. */ -class WifiViewModel @Inject constructor( +@SysUISingleton +class WifiViewModel +@Inject +constructor( constants: WifiConstants, private val context: Context, logger: ConnectivityPipelineLogger, interactor: WifiInteractor, + @Application private val scope: CoroutineScope, statusBarPipelineFlags: StatusBarPipelineFlags, ) { - /** - * The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon. - */ + /** The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon. */ @DrawableRes - private val iconResId: Flow = interactor.wifiNetwork.map { - when (it) { - is WifiNetworkModel.CarrierMerged -> null - is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK - is WifiNetworkModel.Active -> - when { - it.level == null -> null - it.isValidated -> WIFI_FULL_ICONS[it.level] - else -> WIFI_NO_INTERNET_ICONS[it.level] + private val iconResId: Flow = + interactor.wifiNetwork + .map { + when (it) { + is WifiNetworkModel.CarrierMerged -> null + is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK + is WifiNetworkModel.Active -> + when { + it.level == null -> null + it.isValidated -> WIFI_FULL_ICONS[it.level] + else -> WIFI_NO_INTERNET_ICONS[it.level] + } } - } - } + } + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null) /** The content description for the wifi icon. */ - private val contentDescription: Flow = interactor.wifiNetwork.map { - when (it) { - is WifiNetworkModel.CarrierMerged -> null - is WifiNetworkModel.Inactive -> - ContentDescription.Loaded( - "${context.getString(WIFI_NO_CONNECTION)},${context.getString(NO_INTERNET)}" - ) - is WifiNetworkModel.Active -> - when (it.level) { - null -> null - else -> { - val levelDesc = context.getString(WIFI_CONNECTION_STRENGTH[it.level]) - when { - it.isValidated -> ContentDescription.Loaded(levelDesc) - else -> ContentDescription.Loaded( - "$levelDesc,${context.getString(NO_INTERNET)}" - ) + private val contentDescription: Flow = + interactor.wifiNetwork + .map { + when (it) { + is WifiNetworkModel.CarrierMerged -> null + is WifiNetworkModel.Inactive -> + ContentDescription.Loaded( + "${context.getString(WIFI_NO_CONNECTION)}," + + context.getString(NO_INTERNET) + ) + is WifiNetworkModel.Active -> + when (it.level) { + null -> null + else -> { + val levelDesc = + context.getString(WIFI_CONNECTION_STRENGTH[it.level]) + when { + it.isValidated -> ContentDescription.Loaded(levelDesc) + else -> + ContentDescription.Loaded( + "$levelDesc,${context.getString(NO_INTERNET)}" + ) + } + } } - } } - } - } + } + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null) - /** - * The wifi icon that should be displayed. Null if we shouldn't display any icon. - */ - val wifiIcon: Flow = combine( + /** The wifi icon that should be displayed. Null if we shouldn't display any icon. */ + private val wifiIcon: Flow = + combine( interactor.isForceHidden, iconResId, contentDescription, @@ -110,6 +133,7 @@ class WifiViewModel @Inject constructor( else -> Icon.Resource(iconResId, contentDescription) } } + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null) /** The wifi activity status. Null if we shouldn't display the activity status. */ private val activity: Flow = @@ -125,29 +149,53 @@ class WifiViewModel @Inject constructor( } .distinctUntilChanged() .logOutputChange(logger, "activity") + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null) - /** True if the activity in view should be visible. */ - val isActivityInViewVisible: Flow = activity.map { it?.hasActivityIn == true } + private val isActivityInViewVisible: Flow = + activity + .map { it?.hasActivityIn == true } + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false) - /** True if the activity out view should be visible. */ - val isActivityOutViewVisible: Flow = activity.map { it?.hasActivityOut == true } + private val isActivityOutViewVisible: Flow = + activity + .map { it?.hasActivityOut == true } + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false) - /** True if the activity container view should be visible. */ - val isActivityContainerVisible: Flow = - combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut -> - activityIn || activityOut - } + private val isActivityContainerVisible: Flow = + combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut -> + activityIn || activityOut + } + .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false) - // TODO(b/238425913): Update this class to use state flows instead. Right now, we have a ton of - // duplicate activity logs because the cold flows are getting duplicated for the three - // activityVisible flows. + /** A view model for the status bar on the home screen. */ + val home: HomeWifiViewModel = + HomeWifiViewModel( + statusBarPipelineFlags, + wifiIcon, + isActivityInViewVisible, + isActivityOutViewVisible, + isActivityContainerVisible, + ) - /** The tint that should be applied to the icon. */ - val tint: Flow = if (!statusBarPipelineFlags.useNewPipelineDebugColoring()) { - emptyFlow() - } else { - flowOf(Color.CYAN) - } + /** A view model for the status bar on keyguard. */ + val keyguard: KeyguardWifiViewModel = + KeyguardWifiViewModel( + statusBarPipelineFlags, + wifiIcon, + isActivityInViewVisible, + isActivityOutViewVisible, + isActivityContainerVisible, + ) + + /** A view model for the status bar in quick settings. */ + val qs: QsWifiViewModel = + QsWifiViewModel( + statusBarPipelineFlags, + wifiIcon, + isActivityInViewVisible, + isActivityOutViewVisible, + isActivityContainerVisible, + ) companion object { @StringRes diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt index eb907bd924714..39d89bf99af25 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickStatusBarHeaderControllerTest.kt @@ -110,7 +110,7 @@ class QuickStatusBarHeaderControllerTest : SysuiTestCase() { `when`(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController) `when`(variableDateViewControllerFactory.create(any())) .thenReturn(variableDateViewController) - `when`(iconManagerFactory.create(any())).thenReturn(iconManager) + `when`(iconManagerFactory.create(any(), any())).thenReturn(iconManager) `when`(view.resources).thenReturn(mContext.resources) `when`(view.isAttachedToWindow).thenReturn(true) `when`(view.context).thenReturn(context) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt index c4485389d646d..c76d9e7a2b200 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt @@ -176,7 +176,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() { } whenever(view.visibility).thenAnswer { _ -> viewVisibility } - whenever(iconManagerFactory.create(any())).thenReturn(iconManager) + whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager) whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true) whenever(featureFlags.isEnabled(Flags.NEW_HEADER)).thenReturn(true) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt index 5ecfc8eb3649f..90ae693db955e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerTest.kt @@ -97,7 +97,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() { whenever(view.visibility).thenAnswer { _ -> viewVisibility } whenever(variableDateViewControllerFactory.create(any())) .thenReturn(variableDateViewController) - whenever(iconManagerFactory.create(any())).thenReturn(iconManager) + whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager) whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(false) mLargeScreenShadeHeaderController = LargeScreenShadeHeaderController( view, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index ba5f5038c1d97..cfaa4707ef763 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -135,7 +135,7 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); - when(mIconManagerFactory.create(any())).thenReturn(mIconManager); + when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager); allowTestableLooperAsMainThread(); TestableLooper.get(this).runWithLooper(() -> { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index de7db74495af6..34399b80c9f7e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -51,8 +51,6 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import javax.inject.Provider; - @RunWith(AndroidTestingRunner.class) @RunWithLooper @SmallTest @@ -79,8 +77,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { LinearLayout layout = new LinearLayout(mContext); TestDarkIconManager manager = new TestDarkIconManager( layout, + StatusBarLocation.HOME, mock(StatusBarPipelineFlags.class), - () -> mock(WifiViewModel.class), + mock(WifiViewModel.class), mMobileContextProvider, mock(DarkIconDispatcher.class)); testCallOnAdd_forManager(manager); @@ -121,13 +120,15 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { TestDarkIconManager( LinearLayout group, + StatusBarLocation location, StatusBarPipelineFlags statusBarPipelineFlags, - Provider wifiViewModelProvider, + WifiViewModel wifiViewModel, MobileContextProvider contextProvider, DarkIconDispatcher darkIconDispatcher) { super(group, + location, statusBarPipelineFlags, - wifiViewModelProvider, + wifiViewModel, contextProvider, darkIconDispatcher); } @@ -165,8 +166,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { private static class TestIconManager extends IconManager implements TestableIconManager { TestIconManager(ViewGroup group, MobileContextProvider contextProvider) { super(group, + StatusBarLocation.HOME, mock(StatusBarPipelineFlags.class), - () -> mock(WifiViewModel.class), + mock(WifiViewModel.class), contextProvider); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java index 37c8f6285970c..a3c6e95141917 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java @@ -431,7 +431,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest { mOperatorNameViewControllerFactory = mock(OperatorNameViewController.Factory.class); when(mOperatorNameViewControllerFactory.create(any())) .thenReturn(mOperatorNameViewController); - when(mIconManagerFactory.create(any())).thenReturn(mIconManager); + when(mIconManagerFactory.create(any(), any())).thenReturn(mIconManager); mSecureSettings = mock(SecureSettings.class); setUpNotificationIconAreaController(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt index 3c200a5da4fae..cbb4b7e051761 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt @@ -20,6 +20,7 @@ import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.lifecycle.InstantTaskExecutorRule +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.util.Assert import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat @@ -45,7 +46,7 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() { @Test fun constructAndBind_hasCorrectSlot() { val view = ModernStatusBarWifiView.constructAndBind( - context, "slotName", mock() + context, "slotName", mock(), StatusBarLocation.HOME ) assertThat(view.slot).isEqualTo("slotName") diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt index f0ef9d043169c..ca30285338c3b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt @@ -36,12 +36,15 @@ import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking import kotlinx.coroutines.yield +import org.junit.After import org.junit.Before import org.junit.Test import org.mockito.Mock @@ -60,6 +63,7 @@ class WifiViewModelTest : SysuiTestCase() { private lateinit var connectivityRepository: FakeConnectivityRepository private lateinit var wifiRepository: FakeWifiRepository private lateinit var interactor: WifiInteractor + private lateinit var scope: CoroutineScope @Before fun setUp() { @@ -67,20 +71,35 @@ class WifiViewModelTest : SysuiTestCase() { connectivityRepository = FakeConnectivityRepository() wifiRepository = FakeWifiRepository() interactor = WifiInteractor(connectivityRepository, wifiRepository) + scope = CoroutineScope(IMMEDIATE) createAndSetViewModel() } + @After + fun tearDown() { + scope.cancel() + } + + // Note on testing: [WifiViewModel] exposes 3 different instances of + // [LocationBasedWifiViewModel]. In practice, these 3 different instances will get the exact + // same data for icon, activity, etc. flows. So, most of these tests will test just one of the + // instances. There are also some tests that verify all 3 instances received the same data. + @Test fun wifiIcon_forceHidden_outputsNull() = runBlocking(IMMEDIATE) { connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI)) - wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2)) - var latest: Icon? = null + // Start as non-null so we can verify we got the update + var latest: Icon? = Icon.Resource(0, null) val job = underTest + .home .wifiIcon .onEach { latest = it } .launchIn(this) + wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2)) + yield() + assertThat(latest).isNull() job.cancel() @@ -89,14 +108,17 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun wifiIcon_notForceHidden_outputsVisible() = runBlocking(IMMEDIATE) { connectivityRepository.setForceHiddenIcons(setOf()) - wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2)) var latest: Icon? = null val job = underTest + .home .wifiIcon .onEach { latest = it } .launchIn(this) + wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2)) + yield() + assertThat(latest).isInstanceOf(Icon.Resource::class.java) job.cancel() @@ -104,13 +126,15 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun wifiIcon_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) { - wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive) - var latest: Icon? = null val job = underTest - .wifiIcon - .onEach { latest = it } - .launchIn(this) + .home + .wifiIcon + .onEach { latest = it } + .launchIn(this) + + wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive) + yield() assertThat(latest).isInstanceOf(Icon.Resource::class.java) val icon = latest as Icon.Resource @@ -125,14 +149,16 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun wifiIcon_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) { - wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged) - - var latest: Icon? = null + var latest: Icon? = Icon.Resource(0, null) val job = underTest + .home .wifiIcon .onEach { latest = it } .launchIn(this) + wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged) + yield() + assertThat(latest).isNull() job.cancel() @@ -140,14 +166,16 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun wifiIcon_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) { - wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null)) - - var latest: Icon? = null + var latest: Icon? = Icon.Resource(0, null) val job = underTest + .home .wifiIcon .onEach { latest = it } .launchIn(this) + wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null)) + yield() + assertThat(latest).isNull() job.cancel() @@ -155,22 +183,23 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun wifiIcon_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) { - val level = 1 - - wifiRepository.setWifiNetwork( - WifiNetworkModel.Active( - NETWORK_ID, - isValidated = true, - level = level - ) - ) - var latest: Icon? = null val job = underTest + .home .wifiIcon .onEach { latest = it } .launchIn(this) + val level = 1 + wifiRepository.setWifiNetwork( + WifiNetworkModel.Active( + NETWORK_ID, + isValidated = true, + level, + ) + ) + yield() + assertThat(latest).isInstanceOf(Icon.Resource::class.java) val icon = latest as Icon.Resource assertThat(icon.res).isEqualTo(WIFI_FULL_ICONS[level]) @@ -184,22 +213,23 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun wifiIcon_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) { - val level = 4 - - wifiRepository.setWifiNetwork( - WifiNetworkModel.Active( - NETWORK_ID, - isValidated = false, - level = level - ) - ) - var latest: Icon? = null val job = underTest + .home .wifiIcon .onEach { latest = it } .launchIn(this) + val level = 4 + wifiRepository.setWifiNetwork( + WifiNetworkModel.Active( + NETWORK_ID, + isValidated = false, + level, + ) + ) + yield() + assertThat(latest).isInstanceOf(Icon.Resource::class.java) val icon = latest as Icon.Resource assertThat(icon.res).isEqualTo(WIFI_NO_INTERNET_ICONS[level]) @@ -211,6 +241,47 @@ class WifiViewModelTest : SysuiTestCase() { job.cancel() } + @Test + fun wifiIcon_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) { + var latestHome: Icon? = null + val jobHome = underTest + .home + .wifiIcon + .onEach { latestHome = it } + .launchIn(this) + + var latestKeyguard: Icon? = null + val jobKeyguard = underTest + .keyguard + .wifiIcon + .onEach { latestKeyguard = it } + .launchIn(this) + + var latestQs: Icon? = null + val jobQs = underTest + .qs + .wifiIcon + .onEach { latestQs = it } + .launchIn(this) + + wifiRepository.setWifiNetwork( + WifiNetworkModel.Active( + NETWORK_ID, + isValidated = true, + level = 1 + ) + ) + yield() + + assertThat(latestHome).isInstanceOf(Icon.Resource::class.java) + assertThat(latestHome).isEqualTo(latestKeyguard) + assertThat(latestKeyguard).isEqualTo(latestQs) + + jobHome.cancel() + jobKeyguard.cancel() + jobQs.cancel() + } + @Test fun activity_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) { whenever(constants.shouldShowActivityConfig).thenReturn(false) @@ -219,18 +290,21 @@ class WifiViewModelTest : SysuiTestCase() { var activityIn: Boolean? = null val activityInJob = underTest - .isActivityInViewVisible - .onEach { activityIn = it } - .launchIn(this) + .home + .isActivityInViewVisible + .onEach { activityIn = it } + .launchIn(this) var activityOut: Boolean? = null val activityOutJob = underTest + .home .isActivityOutViewVisible .onEach { activityOut = it } .launchIn(this) var activityContainer: Boolean? = null val activityContainerJob = underTest + .home .isActivityContainerVisible .onEach { activityContainer = it } .launchIn(this) @@ -253,18 +327,21 @@ class WifiViewModelTest : SysuiTestCase() { var activityIn: Boolean? = null val activityInJob = underTest + .home .isActivityInViewVisible .onEach { activityIn = it } .launchIn(this) var activityOut: Boolean? = null val activityOutJob = underTest + .home .isActivityOutViewVisible .onEach { activityOut = it } .launchIn(this) var activityContainer: Boolean? = null val activityContainerJob = underTest + .home .isActivityContainerVisible .onEach { activityContainer = it } .launchIn(this) @@ -293,18 +370,21 @@ class WifiViewModelTest : SysuiTestCase() { var activityIn: Boolean? = null val activityInJob = underTest + .home .isActivityInViewVisible .onEach { activityIn = it } .launchIn(this) var activityOut: Boolean? = null val activityOutJob = underTest + .home .isActivityOutViewVisible .onEach { activityOut = it } .launchIn(this) var activityContainer: Boolean? = null val activityContainerJob = underTest + .home .isActivityContainerVisible .onEach { activityContainer = it } .launchIn(this) @@ -324,6 +404,46 @@ class WifiViewModelTest : SysuiTestCase() { activityContainerJob.cancel() } + @Test + fun activity_allLocationViewModelsReceiveSameData() = runBlocking(IMMEDIATE) { + whenever(constants.shouldShowActivityConfig).thenReturn(true) + createAndSetViewModel() + wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK) + + var latestHome: Boolean? = null + val jobHome = underTest + .home + .isActivityInViewVisible + .onEach { latestHome = it } + .launchIn(this) + + var latestKeyguard: Boolean? = null + val jobKeyguard = underTest + .keyguard + .isActivityInViewVisible + .onEach { latestKeyguard = it } + .launchIn(this) + + var latestQs: Boolean? = null + val jobQs = underTest + .qs + .isActivityInViewVisible + .onEach { latestQs = it } + .launchIn(this) + + val activity = WifiActivityModel(hasActivityIn = true, hasActivityOut = true) + wifiRepository.setWifiActivity(activity) + yield() + + assertThat(latestHome).isTrue() + assertThat(latestKeyguard).isTrue() + assertThat(latestQs).isTrue() + + jobHome.cancel() + jobKeyguard.cancel() + jobQs.cancel() + } + @Test fun activityIn_hasActivityInTrue_outputsTrue() = runBlocking(IMMEDIATE) { whenever(constants.shouldShowActivityConfig).thenReturn(true) @@ -332,6 +452,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityInViewVisible .onEach { latest = it } .launchIn(this) @@ -353,6 +474,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityInViewVisible .onEach { latest = it } .launchIn(this) @@ -374,6 +496,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityOutViewVisible .onEach { latest = it } .launchIn(this) @@ -395,6 +518,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityOutViewVisible .onEach { latest = it } .launchIn(this) @@ -416,6 +540,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityContainerVisible .onEach { latest = it } .launchIn(this) @@ -437,6 +562,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityContainerVisible .onEach { latest = it } .launchIn(this) @@ -458,6 +584,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityContainerVisible .onEach { latest = it } .launchIn(this) @@ -479,6 +606,7 @@ class WifiViewModelTest : SysuiTestCase() { var latest: Boolean? = null val job = underTest + .home .isActivityContainerVisible .onEach { latest = it } .launchIn(this) @@ -501,6 +629,7 @@ class WifiViewModelTest : SysuiTestCase() { context, logger, interactor, + scope, statusBarPipelineFlags, ) }