Merge "[Status bar] Delay showing the status bar during dream transition." into udc-dev

This commit is contained in:
Caitlin Shkuratov
2023-05-09 13:10:31 +00:00
committed by Android (Google) Code Review
9 changed files with 336 additions and 19 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.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent; import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
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.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController;
@@ -158,6 +159,7 @@ public abstract class StatusBarViewModule {
StatusBarIconController statusBarIconController, StatusBarIconController statusBarIconController,
StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory, StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
CollapsedStatusBarViewModel collapsedStatusBarViewModel, CollapsedStatusBarViewModel collapsedStatusBarViewModel,
CollapsedStatusBarViewBinder collapsedStatusBarViewBinder,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController, KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController, ShadeViewController shadeViewController,
@@ -182,6 +184,7 @@ public abstract class StatusBarViewModule {
statusBarIconController, statusBarIconController,
darkIconManagerFactory, darkIconManagerFactory,
collapsedStatusBarViewModel, collapsedStatusBarViewModel,
collapsedStatusBarViewBinder,
statusBarHideIconsForBouncerManager, statusBarHideIconsForBouncerManager,
keyguardStateController, keyguardStateController,
shadeViewController, shadeViewController,

View File

@@ -14,8 +14,6 @@
package com.android.systemui.statusbar.phone.fragment; package com.android.systemui.statusbar.phone.fragment;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.IDLE;
import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT; import static com.android.systemui.statusbar.events.SystemStatusAnimationSchedulerKt.SHOWING_PERSISTENT_DOT;
@@ -69,6 +67,7 @@ import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentCom
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallListener; 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.binder.CollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController;
@@ -131,6 +130,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
private final StatusBarIconController mStatusBarIconController; private final StatusBarIconController mStatusBarIconController;
private final CarrierConfigTracker mCarrierConfigTracker; private final CarrierConfigTracker mCarrierConfigTracker;
private final CollapsedStatusBarViewModel mCollapsedStatusBarViewModel; private final CollapsedStatusBarViewModel mCollapsedStatusBarViewModel;
private final CollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
private final StatusBarIconController.DarkIconManager.Factory mDarkIconManagerFactory; private final StatusBarIconController.DarkIconManager.Factory mDarkIconManagerFactory;
private final SecureSettings mSecureSettings; private final SecureSettings mSecureSettings;
@@ -182,12 +182,22 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
*/ */
private boolean mWaitingForWindowStateChangeAfterCameraLaunch = false; private boolean mWaitingForWindowStateChangeAfterCameraLaunch = false;
/**
* True when a transition from lockscreen to dream has started, but haven't yet received a
* status bar window state change afterward.
*
* Similar to [mWaitingForWindowStateChangeAfterCameraLaunch].
*/
private boolean mTransitionFromLockscreenToDreamStarted = false;
/** /**
* Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives * Listener that updates {@link #mWaitingForWindowStateChangeAfterCameraLaunch} when it receives
* a new status bar window state. * a new status bar window state.
*/ */
private final StatusBarWindowStateListener mStatusBarWindowStateListener = state -> private final StatusBarWindowStateListener mStatusBarWindowStateListener = state -> {
mWaitingForWindowStateChangeAfterCameraLaunch = false; mWaitingForWindowStateChangeAfterCameraLaunch = false;
mTransitionFromLockscreenToDreamStarted = false;
};
@SuppressLint("ValidFragment") @SuppressLint("ValidFragment")
public CollapsedStatusBarFragment( public CollapsedStatusBarFragment(
@@ -201,6 +211,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
StatusBarIconController statusBarIconController, StatusBarIconController statusBarIconController,
StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory, StatusBarIconController.DarkIconManager.Factory darkIconManagerFactory,
CollapsedStatusBarViewModel collapsedStatusBarViewModel, CollapsedStatusBarViewModel collapsedStatusBarViewModel,
CollapsedStatusBarViewBinder collapsedStatusBarViewBinder,
StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager, StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
KeyguardStateController keyguardStateController, KeyguardStateController keyguardStateController,
ShadeViewController shadeViewController, ShadeViewController shadeViewController,
@@ -224,6 +235,7 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mFeatureFlags = featureFlags; mFeatureFlags = featureFlags;
mStatusBarIconController = statusBarIconController; mStatusBarIconController = statusBarIconController;
mCollapsedStatusBarViewModel = collapsedStatusBarViewModel; mCollapsedStatusBarViewModel = collapsedStatusBarViewModel;
mCollapsedStatusBarViewBinder = collapsedStatusBarViewBinder;
mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager; mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
mDarkIconManagerFactory = darkIconManagerFactory; mDarkIconManagerFactory = darkIconManagerFactory;
mKeyguardStateController = keyguardStateController; mKeyguardStateController = keyguardStateController;
@@ -296,8 +308,8 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
mCarrierConfigTracker.addCallback(mCarrierConfigCallback); mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener); mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
CollapsedStatusBarViewBinder.bind( mCollapsedStatusBarViewBinder.bind(
mStatusBar, mCollapsedStatusBarViewModel, this::updateStatusBarVisibilities); mStatusBar, mCollapsedStatusBarViewModel, mStatusBarVisibilityChangeListener);
} }
@Override @Override
@@ -411,6 +423,19 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
return mStatusBarFragmentComponent; return mStatusBarFragmentComponent;
} }
private StatusBarVisibilityChangeListener mStatusBarVisibilityChangeListener =
new StatusBarVisibilityChangeListener() {
@Override
public void onStatusBarVisibilityMaybeChanged() {
updateStatusBarVisibilities(/* animate= */ true);
}
@Override
public void onTransitionFromLockscreenToDreamStarted() {
mTransitionFromLockscreenToDreamStarted = true;
}
};
@Override @Override
public void disable(int displayId, int state1, int state2, boolean animate) { public void disable(int displayId, int state1, int state2, boolean animate) {
if (displayId != getContext().getDisplayId()) { if (displayId != getContext().getDisplayId()) {
@@ -423,10 +448,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
updateStatusBarVisibilities(animate); updateStatusBarVisibilities(animate);
} }
private void updateStatusBarVisibilities() {
updateStatusBarVisibilities(/* animate= */ true);
}
private void updateStatusBarVisibilities(boolean animate) { private void updateStatusBarVisibilities(boolean animate) {
StatusBarVisibilityModel previousModel = mLastModifiedVisibility; StatusBarVisibilityModel previousModel = mLastModifiedVisibility;
StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility); StatusBarVisibilityModel newModel = calculateInternalModel(mLastSystemVisibility);
@@ -546,6 +567,18 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
return true; return true;
} }
// Similar to [hideIconsForSecureCamera]: When dream is launched over lockscreen, the icons
// are momentarily visible because the dream animation has finished, but SysUI has not been
// informed that the dream is full-screen. For extra safety, we double-check that we're
// still dreaming.
final boolean hideIconsForDream =
mTransitionFromLockscreenToDreamStarted
&& mKeyguardUpdateMonitor.isDreaming()
&& mKeyguardStateController.isOccluded();
if (hideIconsForDream) {
return true;
}
// While the status bar is transitioning from lockscreen to an occluded, we don't yet know // 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 // 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 // briefly show the status bar just to immediately hide it again. So, we wait for the

View File

@@ -44,6 +44,8 @@ import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerPr
import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxyImpl import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxyImpl
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinderImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
@@ -107,6 +109,11 @@ abstract class StatusBarPipelineModule {
impl: CollapsedStatusBarViewModelImpl impl: CollapsedStatusBarViewModelImpl
): CollapsedStatusBarViewModel ): CollapsedStatusBarViewModel
@Binds
abstract fun collapsedStatusBarViewBinder(
impl: CollapsedStatusBarViewBinderImpl
): CollapsedStatusBarViewBinder
companion object { companion object {
@Provides @Provides
@SysUISingleton @SysUISingleton

View File

@@ -19,34 +19,61 @@ package com.android.systemui.statusbar.pipeline.shared.ui.binder
import android.view.View import android.view.View
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.CollapsedStatusBarViewModel
import javax.inject.Inject
import kotlinx.coroutines.launch
object CollapsedStatusBarViewBinder { /**
* Interface to assist with binding the [CollapsedStatusBarFragment] to
* [CollapsedStatusBarViewModel]. Used only to enable easy testing of [CollapsedStatusBarFragment].
*/
interface CollapsedStatusBarViewBinder {
/** /**
* Binds the view to the view-model. [listener] will be notified whenever an event that may * Binds the view to the view-model. [listener] will be notified whenever an event that may
* change the status bar visibility occurs. * change the status bar visibility occurs.
*/ */
@JvmStatic
fun bind( fun bind(
view: View, view: View,
viewModel: CollapsedStatusBarViewModel, viewModel: CollapsedStatusBarViewModel,
listener: StatusBarVisibilityChangeListener, listener: StatusBarVisibilityChangeListener,
)
}
@SysUISingleton
class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBarViewBinder {
override fun bind(
view: View,
viewModel: CollapsedStatusBarViewModel,
listener: StatusBarVisibilityChangeListener,
) { ) {
view.repeatWhenAttached { view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.CREATED) { repeatOnLifecycle(Lifecycle.State.CREATED) {
viewModel.isTransitioningFromLockscreenToOccluded.collect { launch {
listener.onStatusBarVisibilityMaybeChanged() viewModel.isTransitioningFromLockscreenToOccluded.collect {
listener.onStatusBarVisibilityMaybeChanged()
}
}
launch {
viewModel.transitionFromLockscreenToDreamStartedEvent.collect {
listener.onTransitionFromLockscreenToDreamStarted()
}
} }
} }
} }
} }
} }
/** /** Listener for various events that may affect the status bar's visibility. */
* Listener to be notified when the status bar visibility might have changed due to the device interface StatusBarVisibilityChangeListener {
* moving to a different state. /**
*/ * Called when the status bar visibility might have changed due to the device moving to a
fun interface StatusBarVisibilityChangeListener { * different state.
*/
fun onStatusBarVisibilityMaybeChanged() fun onStatusBarVisibilityMaybeChanged()
/** Called when a transition from lockscreen to dream has started. */
fun onTransitionFromLockscreenToDreamStarted()
} }

View File

@@ -22,8 +22,10 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInterac
import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.shared.model.TransitionState
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
@@ -43,6 +45,9 @@ interface CollapsedStatusBarViewModel {
* otherwise. * otherwise.
*/ */
val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean> val isTransitioningFromLockscreenToOccluded: StateFlow<Boolean>
/** Emits whenever a transition from lockscreen to dream has started. */
val transitionFromLockscreenToDreamStartedEvent: Flow<Unit>
} }
@SysUISingleton @SysUISingleton
@@ -59,4 +64,9 @@ constructor(
it.transitionState == TransitionState.RUNNING it.transitionState == TransitionState.RUNNING
} }
.stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false) .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
override val transitionFromLockscreenToDreamStartedEvent: Flow<Unit> =
keyguardTransitionInteractor.lockscreenToDreamingTransition
.filter { it.transitionState == TransitionState.STARTED }
.map {}
} }

View File

@@ -74,6 +74,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher; 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;
import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController; import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewBinder;
import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel; import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeCollapsedStatusBarViewModel;
import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.window.StatusBarWindowStateController; import com.android.systemui.statusbar.window.StatusBarWindowStateController;
@@ -129,6 +130,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
@Mock @Mock
private StatusBarIconController.DarkIconManager mIconManager; private StatusBarIconController.DarkIconManager mIconManager;
private FakeCollapsedStatusBarViewModel mCollapsedStatusBarViewModel; private FakeCollapsedStatusBarViewModel mCollapsedStatusBarViewModel;
private FakeCollapsedStatusBarViewBinder mCollapsedStatusBarViewBinder;
@Mock @Mock
private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager; private StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
@Mock @Mock
@@ -612,6 +614,55 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
assertEquals(View.VISIBLE, getClockView().getVisibility()); assertEquals(View.VISIBLE, getClockView().getVisibility());
} }
@Test
public void testStatusBarIcons_hiddenThroughoutLockscreenToDreamTransition() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN a transition to dream has started
mCollapsedStatusBarViewBinder.getListener().onTransitionFromLockscreenToDreamStarted();
when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(true);
when(mKeyguardStateController.isOccluded()).thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN status icons should be invisible or gone, but certainly not VISIBLE.
assertNotEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertNotEquals(View.VISIBLE, getClockView().getVisibility());
// WHEN the transition has finished and dream is displaying
mockLockscreenToDreamTransitionFinished();
// (This approximates "dream is displaying")
when(mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer())
.thenReturn(true);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN the views still aren't visible because dream is hiding them
assertNotEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertNotEquals(View.VISIBLE, getClockView().getVisibility());
// WHEN dream has ended
when(mStatusBarHideIconsForBouncerManager.getShouldHideStatusBarIconsForBouncer())
.thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN the views can be visible again
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Test
public void testStatusBarIcons_lockscreenToDreamTransitionButNotDreaming_iconsVisible() {
final CollapsedStatusBarFragment fragment = resumeAndGetFragment();
// WHEN a transition to dream has started but we're *not* dreaming
mCollapsedStatusBarViewBinder.getListener().onTransitionFromLockscreenToDreamStarted();
when(mKeyguardUpdateMonitor.isDreaming()).thenReturn(false);
fragment.disable(DEFAULT_DISPLAY, 0, 0, false);
// THEN the views are still visible
assertEquals(View.VISIBLE, getEndSideContentView().getVisibility());
assertEquals(View.VISIBLE, getClockView().getVisibility());
}
@Override @Override
protected Fragment instantiate(Context context, String className, Bundle arguments) { protected Fragment instantiate(Context context, String className, Bundle arguments) {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
@@ -631,6 +682,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mShadeExpansionStateManager = new ShadeExpansionStateManager(); mShadeExpansionStateManager = new ShadeExpansionStateManager();
mCollapsedStatusBarViewModel = new FakeCollapsedStatusBarViewModel(); mCollapsedStatusBarViewModel = new FakeCollapsedStatusBarViewModel();
mCollapsedStatusBarViewBinder = new FakeCollapsedStatusBarViewBinder();
setUpNotificationIconAreaController(); setUpNotificationIconAreaController();
return new CollapsedStatusBarFragment( return new CollapsedStatusBarFragment(
@@ -644,6 +696,7 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
mStatusBarIconController, mStatusBarIconController,
mIconManagerFactory, mIconManagerFactory,
mCollapsedStatusBarViewModel, mCollapsedStatusBarViewModel,
mCollapsedStatusBarViewBinder,
mStatusBarHideIconsForBouncerManager, mStatusBarHideIconsForBouncerManager,
mKeyguardStateController, mKeyguardStateController,
mShadeViewController, mShadeViewController,
@@ -718,6 +771,12 @@ public class CollapsedStatusBarFragmentTest extends SysuiBaseFragmentTest {
} }
} }
private void mockLockscreenToDreamTransitionFinished() {
for (StatusBarWindowStateListener listener : mStatusBarWindowStateListeners) {
listener.onStatusBarWindowStateChanged(StatusBarManager.WINDOW_STATE_HIDDEN);
}
}
private CollapsedStatusBarFragment resumeAndGetFragment() { private CollapsedStatusBarFragment resumeAndGetFragment() {
mFragments.dispatchResume(); mFragments.dispatchResume();
processAllMessages(); processAllMessages();

View File

@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -176,4 +177,139 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@Test
fun transitionFromLockscreenToDreamStartedEvent_started_emitted() =
testScope.runTest {
val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = 0f,
TransitionState.STARTED,
)
)
assertThat(emissions.size).isEqualTo(1)
}
@Test
fun transitionFromLockscreenToDreamStartedEvent_startedMultiple_emittedMultiple() =
testScope.runTest {
val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = 0f,
TransitionState.STARTED,
)
)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = 0f,
TransitionState.STARTED,
)
)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = 0f,
TransitionState.STARTED,
)
)
assertThat(emissions.size).isEqualTo(3)
}
@Test
fun transitionFromLockscreenToDreamStartedEvent_startedThenRunning_emittedOnlyOne() =
testScope.runTest {
val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = 0f,
TransitionState.STARTED,
)
)
assertThat(emissions.size).isEqualTo(1)
// WHEN the transition progresses through its animation by going through the RUNNING
// step with increasing fractions
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = .1f,
TransitionState.RUNNING,
)
)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = .2f,
TransitionState.RUNNING,
)
)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = .3f,
TransitionState.RUNNING,
)
)
// THEN the flow does not emit since the flow should only emit when the transition
// starts
assertThat(emissions.size).isEqualTo(1)
}
@Test
fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransition_notEmitted() =
testScope.runTest {
val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.OCCLUDED,
value = 0f,
TransitionState.STARTED,
)
)
assertThat(emissions).isEmpty()
}
@Test
fun transitionFromLockscreenToDreamStartedEvent_irrelevantTransitionState_notEmitted() =
testScope.runTest {
val emissions by collectValues(underTest.transitionFromLockscreenToDreamStartedEvent)
keyguardTransitionRepository.sendTransitionStep(
TransitionStep(
KeyguardState.LOCKSCREEN,
KeyguardState.DREAMING,
value = 1.0f,
TransitionState.FINISHED,
)
)
assertThat(emissions).isEmpty()
}
} }

View File

@@ -0,0 +1,39 @@
/*
* 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 android.view.View
import com.android.systemui.statusbar.pipeline.shared.ui.binder.CollapsedStatusBarViewBinder
import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
/**
* A fake view binder that can be used from Java tests.
*
* Since Java tests can't run tests within test scopes, we need to bypass the flows from
* [CollapsedStatusBarViewModel] and just trigger the listener directly.
*/
class FakeCollapsedStatusBarViewBinder : CollapsedStatusBarViewBinder {
var listener: StatusBarVisibilityChangeListener? = null
override fun bind(
view: View,
viewModel: CollapsedStatusBarViewModel,
listener: StatusBarVisibilityChangeListener,
) {
this.listener = listener
}
}

View File

@@ -16,8 +16,11 @@
package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel { class FakeCollapsedStatusBarViewModel : CollapsedStatusBarViewModel {
override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false) override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false)
override val transitionFromLockscreenToDreamStartedEvent = MutableSharedFlow<Unit>()
} }