[SB Refactor] Turn the wifi ViewModel into an @SysUISingleton and
instead create separate view models per location. This CL eliminates the many duplicate logs by: 1) Making WifiViewModel a Singleton, so we only ever have one of each flow. 2) Adding 1 ViewModel class per location, which just references the singleton view model flows. 3) Making each flow inside WifiViewModel a StateFlow, so that its logic (including its logging logic) isn't duplicated each time we re-use one of the flows. Bug: 238425913 Test: manual: Verified wifi icon is tinted different colors in each of the 3 locations Test: manual: Verify wifi icon still updates Test: manual: Verify we don't get duplicate activity logs Test: statusbar.pipeline tests Change-Id: I6ab0245a83858875c4e63baf9bb6a8c482d1fe55
This commit is contained in:
@@ -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<QuickStatusBarHeader
|
||||
mView.requireViewById(R.id.date_clock)
|
||||
);
|
||||
|
||||
mIconManager = tintedIconManagerFactory.create(mIconContainer);
|
||||
mIconManager = tintedIconManagerFactory.create(mIconContainer, StatusBarLocation.QS);
|
||||
mDemoModeReceiver = new ClockDemoModeReceiver(mClockView);
|
||||
mColorExtractor = colorExtractor;
|
||||
mOnColorsChangedListener = (extractor, which) -> {
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
|
||||
@@ -352,8 +352,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat
|
||||
mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
|
||||
mDisableStateTracker.startTracking(mCommandQueue, mView.getDisplay().getDisplayId());
|
||||
if (mTintedIconManager == null) {
|
||||
mTintedIconManager =
|
||||
mTintedIconManagerFactory.create(mView.findViewById(R.id.statusIcons));
|
||||
mTintedIconManager = mTintedIconManagerFactory.create(
|
||||
mView.findViewById(R.id.statusIcons), StatusBarLocation.KEYGUARD);
|
||||
mTintedIconManager.setBlockList(getBlockedIcons());
|
||||
mStatusBarIconController.addIconGroup(mTintedIconManager);
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
|
||||
public interface StatusBarIconController {
|
||||
|
||||
@@ -139,13 +138,15 @@ public interface StatusBarIconController {
|
||||
|
||||
public DarkIconManager(
|
||||
LinearLayout linearLayout,
|
||||
StatusBarLocation location,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> 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<WifiViewModel> mWifiViewModelProvider;
|
||||
private final WifiViewModel mWifiViewModel;
|
||||
private final MobileContextProvider mMobileContextProvider;
|
||||
private final DarkIconDispatcher mDarkIconDispatcher;
|
||||
|
||||
@Inject
|
||||
public Factory(
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> 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<WifiViewModel> 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<WifiViewModel> mWifiViewModelProvider;
|
||||
private final WifiViewModel mWifiViewModel;
|
||||
private final MobileContextProvider mMobileContextProvider;
|
||||
|
||||
@Inject
|
||||
public Factory(
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> 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<WifiViewModel> 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<WifiViewModel> 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) {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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<ImageView>(R.id.wifi_signal)
|
||||
val activityInView = view.requireViewById<ImageView>(R.id.wifi_in)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Icon?>,
|
||||
isActivityInViewVisible: Flow<Boolean>,
|
||||
isActivityOutViewVisible: Flow<Boolean>,
|
||||
isActivityContainerVisible: Flow<Boolean>,
|
||||
) :
|
||||
LocationBasedWifiViewModel(
|
||||
statusBarPipelineFlags,
|
||||
debugTint = Color.CYAN,
|
||||
wifiIcon,
|
||||
isActivityInViewVisible,
|
||||
isActivityOutViewVisible,
|
||||
isActivityContainerVisible,
|
||||
)
|
||||
@@ -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<Icon?>,
|
||||
isActivityInViewVisible: Flow<Boolean>,
|
||||
isActivityOutViewVisible: Flow<Boolean>,
|
||||
isActivityContainerVisible: Flow<Boolean>,
|
||||
) :
|
||||
LocationBasedWifiViewModel(
|
||||
statusBarPipelineFlags,
|
||||
debugTint = Color.MAGENTA,
|
||||
wifiIcon,
|
||||
isActivityInViewVisible,
|
||||
isActivityOutViewVisible,
|
||||
isActivityContainerVisible,
|
||||
)
|
||||
@@ -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<Icon?>,
|
||||
|
||||
/** True if the activity in view should be visible. */
|
||||
val isActivityInViewVisible: Flow<Boolean>,
|
||||
|
||||
/** True if the activity out view should be visible. */
|
||||
val isActivityOutViewVisible: Flow<Boolean>,
|
||||
|
||||
/** True if the activity container view should be visible. */
|
||||
val isActivityContainerVisible: Flow<Boolean>,
|
||||
) {
|
||||
/** The color that should be used to tint the icon. */
|
||||
val tint: Flow<Int> =
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<Icon?>,
|
||||
isActivityInViewVisible: Flow<Boolean>,
|
||||
isActivityOutViewVisible: Flow<Boolean>,
|
||||
isActivityContainerVisible: Flow<Boolean>,
|
||||
) :
|
||||
LocationBasedWifiViewModel(
|
||||
statusBarPipelineFlags,
|
||||
debugTint = Color.GREEN,
|
||||
wifiIcon,
|
||||
isActivityInViewVisible,
|
||||
isActivityOutViewVisible,
|
||||
isActivityContainerVisible,
|
||||
)
|
||||
@@ -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<Int?> = 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<Int?> =
|
||||
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<ContentDescription?> = 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<ContentDescription?> =
|
||||
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<Icon?> = combine(
|
||||
/** The wifi icon that should be displayed. Null if we shouldn't display any icon. */
|
||||
private val wifiIcon: Flow<Icon?> =
|
||||
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<WifiActivityModel?> =
|
||||
@@ -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<Boolean> = activity.map { it?.hasActivityIn == true }
|
||||
private val isActivityInViewVisible: Flow<Boolean> =
|
||||
activity
|
||||
.map { it?.hasActivityIn == true }
|
||||
.stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
|
||||
|
||||
/** True if the activity out view should be visible. */
|
||||
val isActivityOutViewVisible: Flow<Boolean> = activity.map { it?.hasActivityOut == true }
|
||||
private val isActivityOutViewVisible: Flow<Boolean> =
|
||||
activity
|
||||
.map { it?.hasActivityOut == true }
|
||||
.stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = false)
|
||||
|
||||
/** True if the activity container view should be visible. */
|
||||
val isActivityContainerVisible: Flow<Boolean> =
|
||||
combine(isActivityInViewVisible, isActivityOutViewVisible) { activityIn, activityOut ->
|
||||
activityIn || activityOut
|
||||
}
|
||||
private val isActivityContainerVisible: Flow<Boolean> =
|
||||
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<Int> = 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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(() -> {
|
||||
|
||||
@@ -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<WifiViewModel> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user