Merge "[Status Bar] Delay showing the status bar until occlusion finishes." into udc-dev

This commit is contained in:
Caitlin Shkuratov
2023-05-03 17:16:35 +00:00
committed by Android (Google) Code Review
8 changed files with 427 additions and 12 deletions

View File

@@ -53,6 +53,7 @@ import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.util.CarrierConfigTracker;
@@ -156,6 +157,7 @@ public abstract class StatusBarViewModule {
FeatureFlags featureFlags,
StatusBarIconController statusBarIconController,
StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
CollapsedStatusBarViewModel collapsedStatusBarViewModel,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController,
@@ -179,6 +181,7 @@ public abstract class StatusBarViewModule {
featureFlags,
statusBarIconController,
darkIconManagerFactory,
collapsedStatusBarViewModel,
statusBarHideIconsForBouncerManager,
keyguardStateController,
shadeViewController,

View File

@@ -68,6 +68,8 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent.Startable;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener;
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
@@ -128,6 +130,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final ShadeExpansionStateManager mShadeExpansionStateManager;
private final StatusBarIconController mStatusBarIconController;
private final CarrierConfigTracker mCarrierConfigTracker;
private final CollapsedStatusBarViewModel mCollapsedStatusBarViewModel;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final StatusBarIconController.DarkIconManager.Factory mDarkIconManagerFactory;
private final SecureSettings mSecureSettings;
@@ -197,6 +200,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
FeatureFlags featureFlags,
StatusBarIconController statusBarIconController,
StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
CollapsedStatusBarViewModel collapsedStatusBarViewModel,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController,
@@ -219,6 +223,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mShadeExpansionStateManager = shadeExpansionStateManager;
mFeatureFlags = featureFlags;
mStatusBarIconController = statusBarIconController;
mCollapsedStatusBarViewModel = collapsedStatusBarViewModel;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mDarkIconManagerFactory = darkIconManagerFactory;
mKeyguardStateController = keyguardStateController;
@@ -290,6 +295,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
new StatusBarSystemEventAnimator(mEndSideContent, getResources());
mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
CollapsedStatusBarViewBinder.bind(
mStatusBar, mCollapsedStatusBarViewModel, this::updateStatusBarVisibilities);
}
@Override
@@ -415,6 +423,10 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
updateStatusBarVisibilities(animate);
}
private void updateStatusBarVisibilities() {
updateStatusBarVisibilities(/* animate= */ true);
}
private void updateStatusBarVisibilities(boolean animate) {
StatusBarVisibilityModel previousModel = mLastModifiedVisibility;
StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility);
@@ -457,7 +469,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
if (!mKeyguardStateController.isLaunchTransitionFadingAway()
&& !mKeyguardStateController.isKeyguardFadingAway()
&& shouldHideNotificationIcons()
&& shouldHideStatusBar()
&& !(mStatusBarStateController.getState() == StatusBarState.KEYGUARD
&& headsUpVisible)) {
// Hide everything
@@ -505,7 +517,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mOngoingCallController.notifyChipVisibilityChanged(showOngoingCallChip);
}
private boolean shouldHideNotificationIcons() {
private boolean shouldHideStatusBar() {
if (!mShadeExpansionStateManager.isClosed()
&& mShadeViewController.shouldHideStatusBarIconsWhenExpanded()) {
return true;
@@ -521,6 +533,9 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
// icons don't remain hidden somehow) we double check that the camera is still showing, the
// status bar window isn't hidden, and we're still occluded as well, though these checks
// are typically unnecessary.
//
// TODO(b/273314977): Can this be deleted now that we have the
// [isTransitioningFromLockscreenToOccluded] check below?
final boolean hideIconsForSecureCamera =
(mWaitingForWindowStateChangeAfterCameraLaunch ||
!mStatusBarWindowStateController.windowIsShowing()) &&
@@ -531,6 +546,16 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
return true;
}
// While the status bar is transitioning from lockscreen to an occluded, we don't yet know
// if the occluding activity is fullscreen or not. If it *is* fullscreen, we don't want to
// briefly show the status bar just to immediately hide it again. So, we wait for the
// transition to occluding to finish before allowing us to potentially show the status bar
// again. (This status bar is always hidden on keyguard, so it's safe to continue hiding it
// during this transition.) See b/273314977.
if (mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().getValue()) {
return true;
}
return mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer();
}

View File

@@ -23,6 +23,8 @@ import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.log.LogBuffer
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModelImpl
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -100,6 +102,11 @@ abstract class StatusBarPipelineModule {
@ClassKey(CarrierConfigCoreStartable::class)
abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable
@Binds
abstract fun collapsedStatusBarViewModel(
impl: CollapsedStatusBarViewModelImpl
): CollapsedStatusBarViewModel
companion object {
@Provides
@SysUISingleton

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 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.shared.ui.binder
import android.view.View
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
object CollapsedStatusBarViewBinder {
/**
* Binds the view to the view-model. [listener] will be notified whenever an event that may
* change the status bar visibility occurs.
*/
@JvmStatic
fun bind(
view: View,
viewModel: CollapsedStatusBarViewModel,
listener: StatusBarVisibilityChangeListener,
) {
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) {
viewModel.isTransitioningFromLockscreenToOccluded.collect {
listener.onStatusBarVisibilityMaybeChanged()
}
}
}
}
}
/**
* Listener to be notified when the status bar visibility might have changed due to the device
* moving to a different state.
*/
fun interface StatusBarVisibilityChangeListener {
fun onStatusBarVisibilityMaybeChanged()
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2023 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.shared.ui.viewmodel
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.TransitionState
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
/**
* A view model that manages the visibility of the [CollapsedStatusBarFragment] based on the device
* state.
*
* Right now, most of the status bar visibility management is actually in
* [CollapsedStatusBarFragment.calculateInternalModel], which uses
* [CollapsedStatusBarFragment.shouldHideNotificationIcons] and
* [StatusBarHideIconsForBouncerManager]. We should move those pieces of logic to this class instead
* so that it's all in one place and easily testable outside of the fragment.
*/
interface CollapsedStatusBarViewModel {
/**
* True if the device is currently transitioning from lockscreen to occluded and false
* otherwise.
*/
val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean>
}
@SysUISingleton
class CollapsedStatusBarViewModelImpl
@Inject
constructor(
keyguardTransitionInteractor: KeyguardTransitionInteractor,
@Application coroutineScope: CoroutineScope,
) : CollapsedStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> =
keyguardTransitionInteractor.lockscreenToOccludedTransition
.map {
it.transitionState == TransitionState.STARTED ||
it.transitionState == TransitionState.RUNNING
}
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
}

View File

@@ -28,10 +28,13 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Fragment;
@@ -71,6 +74,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
@@ -83,7 +87,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@@ -125,6 +128,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
private StatusBarIconController.DarkIconManager.Factory mIconManagerFactory;
@Mock
private StatusBarIconController.DarkIconManager mIconManager;
private FakeCollapsedStatusBarViewModel mCollapsedStatusBarViewModel;
@Mock
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock
@@ -280,15 +284,15 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
fragment.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NOTIFICATION_ICONS, 0, false);
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
}
@Test
@@ -320,7 +324,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@@ -336,7 +340,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -353,7 +357,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
// THEN all views are hidden
assertEquals(View.INVISIBLE, getClockView().getVisibility());
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the shade is updated to no longer be open
@@ -364,7 +368,58 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@Test
public void disable_notTransitioningToOccluded_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@Test
public void disable_isTransitioningToOccluded_everythingHidden() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
}
@Test
public void disable_wasTransitioningToOccluded_transitionFinished_everythingShown() {
CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN the transition is occurring
mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN all views are hidden
assertEquals(View.GONE, getClockView().getVisibility());
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
assertEquals(View.INVISIBLE, getEndSideContentView().getVisibility());
// WHEN the transition has finished
mCollapsedStatusBarViewModel.isTransitioningFromLockscreenToOccluded().setValue(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN all views are shown
assertEquals(View.VISIBLE, getClockView().getVisibility());
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.VISIBLE));
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
}
@@ -397,7 +452,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE,
mFragment.getView().findViewById(R.id.ongoing_call_chip).getVisibility());
Mockito.verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
verify(mNotificationAreaInner, atLeast(1)).setVisibility(eq(View.INVISIBLE));
}
@Test
@@ -575,6 +630,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mSecureSettings = mock(SecureSettings.class);
mShadeExpansionStateManager = new ShadeExpansionStateManager();
mCollapsedStatusBarViewModel = new FakeCollapsedStatusBarViewModel();
setUpNotificationIconAreaController();
return new CollapsedStatusBarFragment(
@@ -587,6 +643,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mock(FeatureFlags.class),
mStatusBarIconController,
mIconManagerFactory,
mCollapsedStatusBarViewModel,
mStatusBarHideIconsForBouncerManager,
mKeyguardStateController,
mShadeViewController,
@@ -619,7 +676,14 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
when(mNotificationAreaInner.getLayoutParams()).thenReturn(
new FrameLayout.LayoutParams(100, 100));
when(mNotificationAreaInner.animate()).thenReturn(mock(ViewPropertyAnimator.class));
// We should probably start using a real view so that we don't need to mock these methods.
ViewPropertyAnimator viewPropertyAnimator = mock(ViewPropertyAnimator.class);
when(mNotificationAreaInner.animate()).thenReturn(viewPropertyAnimator);
when(viewPropertyAnimator.alpha(anyFloat())).thenReturn(viewPropertyAnimator);
when(viewPropertyAnimator.setDuration(anyLong())).thenReturn(viewPropertyAnimator);
when(viewPropertyAnimator.setInterpolator(any())).thenReturn(viewPropertyAnimator);
when(viewPropertyAnimator.setStartDelay(anyLong())).thenReturn(viewPropertyAnimator);
when(viewPropertyAnimator.withEndAction(any())).thenReturn(viewPropertyAnimator);
when(mMockNotificationAreaController.getNotificationInnerAreaView()).thenReturn(
mNotificationAreaInner);

View File

@@ -0,0 +1,179 @@
/*
* Copyright (C) 2023 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.shared.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
private lateinit var underTest: CollapsedStatusBarViewModel
private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var testScope: TestScope
@Before
fun setUp() {
testScope = TestScope(UnconfinedTestDispatcher())
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val interactor = KeyguardTransitionInteractor(keyguardTransitionRepository)
underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
}
@Test
fun isTransitioningFromLockscreenToOccluded_started_isTrue() =
testScope.runTest {
val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.STARTED,
)
)
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isTrue()
job.cancel()
}
@Test
fun isTransitioningFromLockscreenToOccluded_running_isTrue() =
testScope.runTest {
val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.RUNNING,
)
)
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isTrue()
job.cancel()
}
@Test
fun isTransitioningFromLockscreenToOccluded_finished_isFalse() =
testScope.runTest {
val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.FINISHED,
)
)
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse()
job.cancel()
}
@Test
fun isTransitioningFromLockscreenToOccluded_canceled_isFalse() =
testScope.runTest {
val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.CANCELED,
)
)
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse()
job.cancel()
}
@Test
fun isTransitioningFromLockscreenToOccluded_irrelevantTransition_isFalse() =
testScope.runTest {
val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.AOD,
KeyguardState.LOCKSCREEN,
value = 0f,
TransitionState.RUNNING,
)
)
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse()
job.cancel()
}
@Test
fun isTransitioningFromLockscreenToOccluded_followsRepoUpdates() =
testScope.runTest {
val job = underTest.isTransitioningFromLockscreenToOccluded.launchIn(this)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.RUNNING,
)
)
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isTrue()
// WHEN the repo updates the transition to finished
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.FINISHED,
)
)
// THEN our manager also updates
assertThat(underTest.isTransitioningFromLockscreenToOccluded.value).isFalse()
job.cancel()
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (C) 2023 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.shared.ui.viewmodel
import kotlinx.coroutines.flow.MutableStateFlow
class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false)
}