summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt52
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java84
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt179
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt23
8 files changed, 427 insertions, 12 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 67243b6aed00..4ae2edcd007a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -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,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index 5af8932859c6..0651a7b2363f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -68,6 +68,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();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 19c77e0b0de3..7aa90336e2bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -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
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
new file mode 100644
index 000000000000..9a59851a230a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt
@@ -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()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
new file mode 100644
index 000000000000..edb7e4daca1b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModel.kt
@@ -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)
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
index 21769922c899..10efcd41019c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentTest.java
@@ -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);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
new file mode 100644
index 000000000000..5faed9d952c2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -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()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
new file mode 100644
index 000000000000..cbf6637ad46b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeCollapsedStatusBarViewModel.kt
@@ -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)
+}