diff options
10 files changed, 287 insertions, 31 deletions
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml index 808425435efa..f22e79722e78 100644 --- a/packages/SystemUI/res/values/ids.xml +++ b/packages/SystemUI/res/values/ids.xml @@ -131,6 +131,9 @@ <!-- For StatusIconContainer to tag its icon views --> <item type="id" name="status_bar_view_state_tag" /> + <!-- Status bar --> + <item type="id" name="status_bar_dot" /> + <!-- Default display cutout on the physical top of screen --> <item type="id" name="display_cutout" /> <item type="id" name="display_cutout_left" /> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java index cf7283c6442e..1196211bd671 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java @@ -38,7 +38,15 @@ public interface StatusIconDisplayable extends DarkReceiver { @StatusBarIconView.VisibleState int getVisibleState(); + /** + * Returns true if this icon should be visible if there's space, and false otherwise. + * + * Note that this doesn't necessarily mean it *will* be visible. It's possible that there are + * more icons than space, in which case this icon might just show a dot or might be completely + * hidden. {@link #getVisibleState} will return the icon's actual visible status. + */ boolean isIconVisible(); + default boolean isIconBlocked() { return false; } 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 c3a9b90c5a62..273be63eb8a2 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,10 +26,15 @@ 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.StatusBarIconView +import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT +import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN +import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON 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.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch @@ -45,6 +50,19 @@ import kotlinx.coroutines.launch object WifiViewBinder { /** + * Defines interface for an object that acts as the binding between the view and its view-model. + * + * Users of the [WifiViewBinder] class should use this to control the binder after it is bound. + */ + interface Binding { + /** Returns true if the wifi icon should be visible and false otherwise. */ + fun getShouldIconBeVisible(): Boolean + + /** Notifies that the visibility state has changed. */ + fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) + } + + /** * 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. */ @@ -53,8 +71,8 @@ object WifiViewBinder { view: ViewGroup, wifiViewModel: WifiViewModel, location: StatusBarLocation, - ) { - when (location) { + ): Binding { + return when (location) { StatusBarLocation.HOME -> bind(view, wifiViewModel.home) StatusBarLocation.KEYGUARD -> bind(view, wifiViewModel.keyguard) StatusBarLocation.QS -> bind(view, wifiViewModel.qs) @@ -66,8 +84,10 @@ object WifiViewBinder { private fun bind( view: ViewGroup, viewModel: LocationBasedWifiViewModel, - ) { + ): Binding { + val groupView = view.requireViewById<ViewGroup>(R.id.wifi_group) val iconView = view.requireViewById<ImageView>(R.id.wifi_signal) + val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot) val activityInView = view.requireViewById<ImageView>(R.id.wifi_in) val activityOutView = view.requireViewById<ImageView>(R.id.wifi_out) val activityContainerView = view.requireViewById<View>(R.id.inout_container) @@ -75,14 +95,21 @@ object WifiViewBinder { view.isVisible = true iconView.isVisible = true + // TODO(b/238425913): We should log this visibility state. + @StatusBarIconView.VisibleState + val visibilityState: MutableStateFlow<Int> = MutableStateFlow(STATE_HIDDEN) + view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { - viewModel.wifiIcon.distinctUntilChanged().collect { wifiIcon -> - // TODO(b/238425913): Right now, if !isVisible, there's just an empty space - // where the wifi icon would be. We need to pipe isVisible through to - // [ModernStatusBarWifiView.isIconVisible], which is what actually makes - // the view GONE. + visibilityState.collect { visibilityState -> + groupView.isVisible = visibilityState == STATE_ICON + dotView.isVisible = visibilityState == STATE_DOT + } + } + + launch { + viewModel.wifiIcon.collect { wifiIcon -> view.isVisible = wifiIcon != null wifiIcon?.let { IconViewBinder.bind(wifiIcon, iconView) } } @@ -94,6 +121,7 @@ object WifiViewBinder { iconView.imageTintList = tintList activityInView.imageTintList = tintList activityOutView.imageTintList = tintList + dotView.setDecorColor(tint) } } @@ -116,5 +144,15 @@ object WifiViewBinder { } } } + + return object : Binding { + override fun getShouldIconBeVisible(): Boolean { + return viewModel.wifiIcon.value != null + } + + override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) { + visibilityState.value = state + } + } } } 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 b874fb10fd1d..6c616ac7c3b8 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 @@ -19,11 +19,13 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.view import android.content.Context import android.graphics.Rect import android.util.AttributeSet +import android.view.Gravity import android.view.LayoutInflater import com.android.systemui.R import com.android.systemui.statusbar.BaseStatusBarWifiView import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON +import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT +import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN 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 @@ -38,6 +40,17 @@ class ModernStatusBarWifiView( ) : BaseStatusBarWifiView(context, attrs) { private lateinit var slot: String + private lateinit var binding: WifiViewBinder.Binding + + @StatusBarIconView.VisibleState + private var iconVisibleState: Int = STATE_HIDDEN + set(value) { + if (field == value) { + return + } + field = value + binding.onVisibilityStateChanged(value) + } override fun onDarkChanged(areas: ArrayList<Rect>?, darkIntensity: Float, tint: Int) { // TODO(b/238425913) @@ -54,23 +67,44 @@ class ModernStatusBarWifiView( } override fun setVisibleState(@StatusBarIconView.VisibleState state: Int, animate: Boolean) { - // TODO(b/238425913) + iconVisibleState = state } @StatusBarIconView.VisibleState override fun getVisibleState(): Int { - // TODO(b/238425913) - return STATE_ICON + return iconVisibleState } override fun isIconVisible(): Boolean { - // TODO(b/238425913) - return true + return binding.getShouldIconBeVisible() + } + + private fun initView( + slotName: String, + wifiViewModel: WifiViewModel, + location: StatusBarLocation, + ) { + slot = slotName + initDotView() + binding = WifiViewBinder.bind(this, wifiViewModel, location) } - /** Set the slot name for this view. */ - private fun setSlot(slotName: String) { - this.slot = slotName + // Mostly duplicated from [com.android.systemui.statusbar.StatusBarWifiView]. + private fun initDotView() { + // TODO(b/238425913): Could we just have this dot view be part of + // R.layout.new_status_bar_wifi_group with a dot drawable so we don't need to inflate it + // manually? Would that not work with animations? + val dotView = StatusBarIconView(mContext, slot, null).also { + it.id = R.id.status_bar_dot + // Hard-code this view to always be in the DOT state so that whenever it's visible it + // will show a dot + it.visibleState = STATE_DOT + } + + val width = mContext.resources.getDimensionPixelSize(R.dimen.status_bar_icon_size) + val lp = LayoutParams(width, width) + lp.gravity = Gravity.CENTER_VERTICAL or Gravity.START + addView(dotView, lp) } companion object { @@ -89,8 +123,7 @@ class ModernStatusBarWifiView( LayoutInflater.from(context).inflate(R.layout.new_status_bar_wifi_group, null) as ModernStatusBarWifiView ).also { - it.setSlot(slot) - WifiViewBinder.bind(it, wifiViewModel, location) + it.initView(slot, 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 index 0847e6214337..871b395d0996 100644 --- 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 @@ -20,6 +20,7 @@ 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.StateFlow /** * A view model for the wifi icon shown on the "home" page (aka, when the device is unlocked and not @@ -27,7 +28,7 @@ import kotlinx.coroutines.flow.Flow */ class HomeWifiViewModel( statusBarPipelineFlags: StatusBarPipelineFlags, - wifiIcon: Flow<Icon?>, + wifiIcon: StateFlow<Icon?>, isActivityInViewVisible: Flow<Boolean>, isActivityOutViewVisible: Flow<Boolean>, isActivityContainerVisible: Flow<Boolean>, 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 index 3f7c8e13d7f7..be1f3f2194bc 100644 --- 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 @@ -20,11 +20,12 @@ 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.StateFlow /** A view model for the wifi icon shown on keyguard (lockscreen). */ class KeyguardWifiViewModel( statusBarPipelineFlags: StatusBarPipelineFlags, - wifiIcon: Flow<Icon?>, + wifiIcon: StateFlow<Icon?>, isActivityInViewVisible: Flow<Boolean>, isActivityOutViewVisible: Flow<Boolean>, isActivityContainerVisible: Flow<Boolean>, 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 index d34ba88cde1a..7243acfbd56d 100644 --- 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 @@ -20,6 +20,7 @@ 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.StateFlow import kotlinx.coroutines.flow.flowOf /** @@ -33,7 +34,7 @@ abstract class LocationBasedWifiViewModel( debugTint: Int, /** The wifi icon that should be displayed. Null if we shouldn't display any icon. */ - val wifiIcon: Flow<Icon?>, + val wifiIcon: StateFlow<Icon?>, /** True if the activity in view should be visible. */ val isActivityInViewVisible: Flow<Boolean>, 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 index 8f6c26a6620f..d640d33eb316 100644 --- 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 @@ -20,11 +20,12 @@ 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.StateFlow /** A view model for the wifi icon shown in quick settings (when the shade is pulled down). */ class QsWifiViewModel( statusBarPipelineFlags: StatusBarPipelineFlags, - wifiIcon: Flow<Icon?>, + wifiIcon: StateFlow<Icon?>, isActivityInViewVisible: Flow<Boolean>, isActivityOutViewVisible: Flow<Boolean>, isActivityContainerVisible: Flow<Boolean>, 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 13fd922720f6..465f5097235e 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 @@ -41,6 +41,7 @@ import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf @@ -120,7 +121,7 @@ constructor( .stateIn(scope, started = SharingStarted.WhileSubscribed(), initialValue = null) /** The wifi icon that should be displayed. Null if we shouldn't display any icon. */ - private val wifiIcon: Flow<Icon?> = + private val wifiIcon: StateFlow<Icon?> = combine( interactor.isEnabled, interactor.isForceHidden, 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 cbb4b7e05176..a93b6b28e3b3 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 @@ -16,39 +16,208 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.view +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper import android.testing.TestableLooper.RunWithLooper +import android.testing.ViewUtils +import android.view.View import androidx.test.filters.SmallTest +import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.lifecycle.InstantTaskExecutorRule +import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT +import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN +import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON import com.android.systemui.statusbar.phone.StatusBarLocation -import com.android.systemui.util.Assert -import com.android.systemui.util.mockito.mock +import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags +import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor +import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants +import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.MockitoAnnotations @SmallTest -@RunWith(JUnit4::class) -@RunWithLooper +@RunWith(AndroidTestingRunner::class) +@RunWithLooper(setAsMainLooper = true) class ModernStatusBarWifiViewTest : SysuiTestCase() { + private lateinit var testableLooper: TestableLooper + + @Mock + private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags + @Mock + private lateinit var logger: ConnectivityPipelineLogger + @Mock + private lateinit var constants: WifiConstants + private lateinit var connectivityRepository: FakeConnectivityRepository + private lateinit var wifiRepository: FakeWifiRepository + private lateinit var interactor: WifiInteractor + private lateinit var viewModel: WifiViewModel + private lateinit var scope: CoroutineScope + @JvmField @Rule val instantTaskExecutor = InstantTaskExecutorRule() @Before fun setUp() { - Assert.setTestThread(Thread.currentThread()) + MockitoAnnotations.initMocks(this) + testableLooper = TestableLooper.get(this) + + connectivityRepository = FakeConnectivityRepository() + wifiRepository = FakeWifiRepository() + wifiRepository.setIsWifiEnabled(true) + interactor = WifiInteractor(connectivityRepository, wifiRepository) + scope = CoroutineScope(Dispatchers.Unconfined) + viewModel = WifiViewModel( + constants, context, logger, interactor, scope, statusBarPipelineFlags + ) } @Test fun constructAndBind_hasCorrectSlot() { val view = ModernStatusBarWifiView.constructAndBind( - context, "slotName", mock(), StatusBarLocation.HOME + context, "slotName", viewModel, StatusBarLocation.HOME ) assertThat(view.slot).isEqualTo("slotName") } + + @Test + fun getVisibleState_icon_returnsIcon() { + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + view.setVisibleState(STATE_ICON, /* animate= */ false) + + assertThat(view.visibleState).isEqualTo(STATE_ICON) + } + + @Test + fun getVisibleState_dot_returnsDot() { + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + view.setVisibleState(STATE_DOT, /* animate= */ false) + + assertThat(view.visibleState).isEqualTo(STATE_DOT) + } + + @Test + fun getVisibleState_hidden_returnsHidden() { + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + view.setVisibleState(STATE_HIDDEN, /* animate= */ false) + + assertThat(view.visibleState).isEqualTo(STATE_HIDDEN) + } + + // Note: The following tests are more like integration tests, since they stand up a full + // [WifiViewModel] and test the interactions between the view, view-binder, and view-model. + + @Test + fun setVisibleState_icon_iconShownDotHidden() { + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + view.setVisibleState(STATE_ICON, /* animate= */ false) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.getIconGroupView().visibility).isEqualTo(View.VISIBLE) + assertThat(view.getDotView().visibility).isEqualTo(View.GONE) + + ViewUtils.detachView(view) + } + + @Test + fun setVisibleState_dot_iconHiddenDotShown() { + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + view.setVisibleState(STATE_DOT, /* animate= */ false) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.getIconGroupView().visibility).isEqualTo(View.GONE) + assertThat(view.getDotView().visibility).isEqualTo(View.VISIBLE) + + ViewUtils.detachView(view) + } + + @Test + fun setVisibleState_hidden_iconAndDotHidden() { + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + view.setVisibleState(STATE_HIDDEN, /* animate= */ false) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.getIconGroupView().visibility).isEqualTo(View.GONE) + assertThat(view.getDotView().visibility).isEqualTo(View.GONE) + + ViewUtils.detachView(view) + } + + @Test + fun isIconVisible_notEnabled_outputsFalse() { + wifiRepository.setIsWifiEnabled(false) + + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.isIconVisible).isFalse() + + ViewUtils.detachView(view) + } + + @Test + fun isIconVisible_enabled_outputsTrue() { + wifiRepository.setIsWifiEnabled(true) + + val view = ModernStatusBarWifiView.constructAndBind( + context, SLOT_NAME, viewModel, StatusBarLocation.HOME + ) + + ViewUtils.attachView(view) + testableLooper.processAllMessages() + + assertThat(view.isIconVisible).isTrue() + + ViewUtils.detachView(view) + } + + private fun View.getIconGroupView(): View { + return this.requireViewById(R.id.wifi_group) + } + + private fun View.getDotView(): View { + return this.requireViewById(R.id.status_bar_dot) + } } + +private const val SLOT_NAME = "TestSlotName" |