summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Christian Göllner <chrisgollner@google.com> 2022-01-06 16:52:59 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-01-06 16:52:59 +0000
commit51a1afe4e66e2e4f5c2c422e785e4e01bf2f625b (patch)
treea7206b8161bf0aa509362762071ead320319bab2
parent06425229da05a0603e9cfd5416fcc9ce4112cef0 (diff)
parentab7ad3e890ab6b7c003ed0e589380639774581b7 (diff)
Merge "Preserve shade state on fold/unfold."
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt130
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java50
7 files changed, 278 insertions, 4 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
new file mode 100644
index 000000000000..56b0d598a512
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FoldStateListener.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.content.Context
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback
+import com.android.internal.R
+
+/**
+ * Listens for fold state changes and reports the new folded state together with other properties
+ * associated with that state.
+ */
+internal class FoldStateListener(
+ context: Context,
+ private val listener: OnFoldStateChangeListener
+) : DeviceStateCallback {
+
+ internal interface OnFoldStateChangeListener {
+ /**
+ * Reports that the device either became folded or unfolded.
+ *
+ * @param folded whether the device is folded.
+ * @param willGoToSleep whether the device will go to sleep and keep the screen off.
+ */
+ fun onFoldStateChanged(folded: Boolean, willGoToSleep: Boolean)
+ }
+
+ private val foldedDeviceStates: IntArray =
+ context.resources.getIntArray(R.array.config_foldedDeviceStates)
+ private val goToSleepDeviceStates: IntArray =
+ context.resources.getIntArray(R.array.config_deviceStatesOnWhichToSleep)
+
+ private var wasFolded: Boolean? = null
+
+ override fun onStateChanged(state: Int) {
+ val isFolded = foldedDeviceStates.contains(state)
+ if (wasFolded == isFolded) {
+ return
+ }
+ wasFolded = isFolded
+ val willGoToSleep = goToSleepDeviceStates.contains(state)
+ listener.onFoldStateChanged(isFolded, willGoToSleep)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index 2d41e5e9be2c..24bb7f25f2eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -55,6 +55,9 @@ public interface ShadeController {
*/
boolean closeShadeIfOpen();
+ /** Returns whether the shade is currently open or opening. */
+ boolean isShadeOpen();
+
/**
* Add a runnable for NotificationPanelView to post when the panel is expanded.
*
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
index b4fed2ba8624..f8b0535b7ec7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeControllerImpl.java
@@ -148,6 +148,13 @@ public class ShadeControllerImpl implements ShadeController {
}
@Override
+ public boolean isShadeOpen() {
+ NotificationPanelViewController controller =
+ getNotificationPanelViewController();
+ return controller.isExpanding() || controller.isFullyExpanded();
+ }
+
+ @Override
public void postOnShadeExpanded(Runnable executable) {
getNotificationPanelViewController().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 3671bb99b451..1cc5e12a4102 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -70,6 +70,7 @@ import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.graphics.Point;
import android.graphics.PointF;
+import android.hardware.devicestate.DeviceStateManager;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.Bundle;
@@ -779,7 +780,8 @@ public class StatusBar extends CoreStartable implements
Optional<StartingSurface> startingSurfaceOptional,
ActivityLaunchAnimator activityLaunchAnimator,
NotifPipelineFlags notifPipelineFlags,
- InteractionJankMonitor jankMonitor) {
+ InteractionJankMonitor jankMonitor,
+ DeviceStateManager deviceStateManager) {
super(context);
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -902,6 +904,9 @@ public class StatusBar extends CoreStartable implements
data -> mCommandQueueCallbacks.animateExpandSettingsPanel(data.mSubpanel));
mMessageRouter.subscribeTo(MSG_LAUNCH_TRANSITION_TIMEOUT,
id -> onLaunchTransitionTimeout());
+
+ deviceStateManager.registerCallback(mMainExecutor,
+ new FoldStateListener(mContext, this::onFoldedStateChanged));
}
@Override
@@ -1100,6 +1105,27 @@ public class StatusBar extends CoreStartable implements
requestTopUi, componentTag))));
}
+ private void onFoldedStateChanged(boolean isFolded, boolean willGoToSleep) {
+ // Folded state changes are followed by a screen off event.
+ // By default turning off the screen also closes the shade.
+ // We want to make sure that the shade status is kept after
+ // folding/unfolding.
+ boolean isShadeOpen = mShadeController.isShadeOpen();
+ boolean leaveOpen = isShadeOpen && !willGoToSleep;
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "#onFoldedStateChanged(): "
+ + "isFolded=%s, "
+ + "willGoToSleep=%s, "
+ + "isShadeOpen=%s, "
+ + "leaveOpen=%s",
+ isFolded, willGoToSleep, isShadeOpen, leaveOpen));
+ }
+ if (leaveOpen) {
+ mStatusBarStateController.setLeaveOpenOnKeyguardHide(true);
+ }
+ }
+
// ================================================================================
// Constructing the view
// ================================================================================
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 50176fec4ac8..977fe9c2d201 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
import android.app.WallpaperManager;
import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
import android.os.Handler;
import android.os.PowerManager;
import android.util.DisplayMetrics;
@@ -231,7 +232,8 @@ public interface StatusBarPhoneModule {
Optional<StartingSurface> startingSurfaceOptional,
ActivityLaunchAnimator activityLaunchAnimator,
NotifPipelineFlags notifPipelineFlags,
- InteractionJankMonitor jankMonitor) {
+ InteractionJankMonitor jankMonitor,
+ DeviceStateManager deviceStateManager) {
return new StatusBar(
context,
notificationsController,
@@ -327,7 +329,8 @@ public interface StatusBarPhoneModule {
startingSurfaceOptional,
activityLaunchAnimator,
notifPipelineFlags,
- jankMonitor
+ jankMonitor,
+ deviceStateManager
);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
new file mode 100644
index 000000000000..649dc235f398
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/FoldStateListenerTest.kt
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar.phone
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.FoldStateListener.OnFoldStateChangeListener
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations.initMocks
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class FoldStateListenerTest : SysuiTestCase() {
+
+ @Mock
+ private lateinit var listener: OnFoldStateChangeListener
+ private lateinit var sut: FoldStateListener
+
+ @Before
+ fun setUp() {
+ initMocks(this)
+ setFoldedStates(DEVICE_STATE_FOLDED)
+ setGoToSleepStates(DEVICE_STATE_FOLDED)
+ sut = FoldStateListener(mContext, listener)
+ }
+
+ @Test
+ fun onStateChanged_stateFolded_notifiesWithFoldedAndGoingToSleep() {
+ sut.onStateChanged(DEVICE_STATE_FOLDED)
+
+ verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateHalfFolded_notifiesWithNotFoldedAndNotGoingToSleep() {
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+ verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateUnfolded_notifiesWithNotFoldedAndNotGoingToSleep() {
+ sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+
+ verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateUnfoldedThenHalfFolded_notifiesOnce() {
+ sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+ verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateHalfFoldedThenUnfolded_notifiesOnce() {
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onStateChanged(DEVICE_STATE_UNFOLDED)
+
+ verify(listener, times(1)).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateHalfFoldedThenFolded_notifiesTwice() {
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+ sut.onStateChanged(DEVICE_STATE_FOLDED)
+
+ val inOrder = Mockito.inOrder(listener)
+ inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+ }
+
+ @Test
+ fun onStateChanged_stateFoldedThenHalfFolded_notifiesTwice() {
+ sut.onStateChanged(DEVICE_STATE_FOLDED)
+ sut.onStateChanged(DEVICE_STATE_HALF_FOLDED)
+
+ val inOrder = Mockito.inOrder(listener)
+ inOrder.verify(listener).onFoldStateChanged(FOLDED, WILL_GO_TO_SLEEP)
+ inOrder.verify(listener).onFoldStateChanged(NOT_FOLDED, WILL_NOT_SLEEP)
+ }
+
+ private fun setGoToSleepStates(vararg states: Int) {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_deviceStatesOnWhichToSleep,
+ states
+ )
+ }
+
+ private fun setFoldedStates(vararg states: Int) {
+ mContext.orCreateTestableResources.addOverride(
+ R.array.config_foldedDeviceStates,
+ states
+ )
+ }
+
+ companion object {
+ private const val DEVICE_STATE_FOLDED = 123
+ private const val DEVICE_STATE_HALF_FOLDED = 456
+ private const val DEVICE_STATE_UNFOLDED = 789
+
+ private const val FOLDED = true
+ private const val NOT_FOLDED = false
+
+ private const val WILL_GO_TO_SLEEP = true
+ private const val WILL_NOT_SLEEP = false
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 69f2bd788e03..bb8bad39ab31 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -44,6 +44,7 @@ import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.IntentFilter;
+import android.hardware.devicestate.DeviceStateManager;
import android.hardware.display.AmbientDisplayConfiguration;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
@@ -162,6 +163,7 @@ import com.android.wm.shell.startingsurface.StartingSurface;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -176,6 +178,9 @@ import dagger.Lazy;
@RunWithLooper(setAsMainLooper = true)
public class StatusBarTest extends SysuiTestCase {
+ private static final int FOLD_STATE_FOLDED = 0;
+ private static final int FOLD_STATE_UNFOLDED = 1;
+
private StatusBar mStatusBar;
private FakeMetricsLogger mMetricsLogger;
private PowerManager mPowerManager;
@@ -281,6 +286,7 @@ public class StatusBarTest extends SysuiTestCase {
@Mock private NotifPipelineFlags mNotifPipelineFlags;
@Mock private NotifLiveDataStore mNotifLiveDataStore;
@Mock private InteractionJankMonitor mJankMonitor;
+ @Mock private DeviceStateManager mDeviceStateManager;
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -466,7 +472,8 @@ public class StatusBarTest extends SysuiTestCase {
Optional.of(mStartingSurface),
mActivityLaunchAnimator,
mNotifPipelineFlags,
- mJankMonitor);
+ mJankMonitor,
+ mDeviceStateManager);
when(mKeyguardViewMediator.registerStatusBar(
any(StatusBar.class),
any(NotificationPanelViewController.class),
@@ -949,6 +956,47 @@ public class StatusBarTest extends SysuiTestCase {
verify(mStatusBarKeyguardViewManager).updateResources();
}
+ @Test
+ public void deviceStateChange_unfolded_shadeOpen_setsLeaveOpenOnKeyguardHide() {
+ setFoldedStates(FOLD_STATE_FOLDED);
+ setGoToSleepStates(FOLD_STATE_FOLDED);
+ when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(true);
+
+ setDeviceState(FOLD_STATE_UNFOLDED);
+
+ verify(mStatusBarStateController).setLeaveOpenOnKeyguardHide(true);
+ }
+
+ @Test
+ public void deviceStateChange_unfolded_shadeClose_doesNotSetLeaveOpenOnKeyguardHide() {
+ setFoldedStates(FOLD_STATE_FOLDED);
+ setGoToSleepStates(FOLD_STATE_FOLDED);
+ when(mNotificationPanelViewController.isFullyExpanded()).thenReturn(false);
+
+ setDeviceState(FOLD_STATE_UNFOLDED);
+
+ verify(mStatusBarStateController, never()).setLeaveOpenOnKeyguardHide(true);
+ }
+
+ private void setDeviceState(int state) {
+ ArgumentCaptor<DeviceStateManager.DeviceStateCallback> callbackCaptor =
+ ArgumentCaptor.forClass(DeviceStateManager.DeviceStateCallback.class);
+ verify(mDeviceStateManager).registerCallback(any(), callbackCaptor.capture());
+ callbackCaptor.getValue().onStateChanged(state);
+ }
+
+ private void setGoToSleepStates(int... states) {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_deviceStatesOnWhichToSleep,
+ states);
+ }
+
+ private void setFoldedStates(int... states) {
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.array.config_foldedDeviceStates,
+ states);
+ }
+
public static class TestableNotificationInterruptStateProviderImpl extends
NotificationInterruptStateProviderImpl {