Merge "Cancel divider dragging if IME showing" into tm-qpr-dev
diff --git a/core/java/android/app/DisabledWallpaperManager.java b/core/java/android/app/DisabledWallpaperManager.java
index 0d14c0b..fae6887 100644
--- a/core/java/android/app/DisabledWallpaperManager.java
+++ b/core/java/android/app/DisabledWallpaperManager.java
@@ -188,17 +188,17 @@
}
@Override
- public WallpaperInfo getWallpaperInfo(int userId) {
+ public WallpaperInfo getWallpaperInfoForUser(int userId) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) {
return unsupported();
}
@Override
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) {
return unsupported();
}
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index c99fa3d..14fe522 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1324,7 +1324,7 @@
* wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
*/
public WallpaperInfo getWallpaperInfo() {
- return getWallpaperInfo(mContext.getUserId());
+ return getWallpaperInfoForUser(mContext.getUserId());
}
/**
@@ -1334,7 +1334,7 @@
* @param userId Owner of the wallpaper.
* @hide
*/
- public WallpaperInfo getWallpaperInfo(int userId) {
+ public WallpaperInfo getWallpaperInfoForUser(int userId) {
try {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
@@ -1349,25 +1349,31 @@
/**
* Returns the information about the home screen wallpaper if its current wallpaper is a live
- * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, this
+ * returns null.
*
- * @param which Specifies wallpaper destination (home or lock).
+ * @param which Specifies wallpaper to request (home or lock).
+ * @throws IllegalArgumentException if {@code which} is not exactly one of
+ * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}.
* @hide
*/
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which) {
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which) {
return getWallpaperInfo();
}
/**
* Returns the information about the designated wallpaper if its current wallpaper is a live
- * wallpaper component. Otherwise, if the wallpaper is a static image, this returns null.
+ * wallpaper component. Otherwise, if the wallpaper is a static image or is not set, this
+ * returns null.
*
- * @param which Specifies wallpaper destination (home or lock).
+ * @param which Specifies wallpaper to request (home or lock).
* @param userId Owner of the wallpaper.
+ * @throws IllegalArgumentException if {@code which} is not exactly one of
+ * {{@link #FLAG_SYSTEM},{@link #FLAG_LOCK}}.
* @hide
*/
- public WallpaperInfo getWallpaperInfoWithFlags(@SetWallpaperFlags int which, int userId) {
- return getWallpaperInfo(userId);
+ public WallpaperInfo getWallpaperInfo(@SetWallpaperFlags int which, int userId) {
+ return getWallpaperInfoForUser(userId);
}
/**
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
index 97ec594..9bad0de 100644
--- a/core/java/android/os/BatteryManagerInternal.java
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -47,6 +47,14 @@
public abstract int getBatteryLevel();
/**
+ * Returns battery health status as an integer representing the current battery health constant.
+ *
+ * This is a simple accessor that's safe to be called from any locks, but internally it may
+ * wait on the battery service lock.
+ */
+ public abstract int getBatteryHealth();
+
+ /**
* Instantaneous battery capacity in uA-h, as defined in the HealthInfo HAL struct.
* Please note apparently it could be bigger than {@link #getBatteryFullCharge}.
*
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index cd38e8a..7c2f952 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -58,4 +58,39 @@
* @param isScreenOn True if the screen is currently on.
*/
public abstract boolean canStartDreaming(boolean isScreenOn);
+
+ /**
+ * Return whether dreams can continue when undocking by default. Even if the default is true,
+ * it can be overridden temporarily, in which case {@link DreamManagerStateListener} will be
+ * informed of any changes.
+ */
+ public abstract boolean keepDreamingWhenUndockedDefault();
+
+ /**
+ * Register a {@link DreamManagerStateListener}, which will be called when there are changes to
+ * dream state.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerDreamManagerStateListener(DreamManagerStateListener listener);
+
+ /**
+ * Unregister a {@link DreamManagerStateListener}, which will be called when there are changes
+ * to dream state.
+ *
+ * @param listener The listener to unregister.
+ */
+ public abstract void unregisterDreamManagerStateListener(DreamManagerStateListener listener);
+
+ /**
+ * Called when there are changes to dream state.
+ */
+ public interface DreamManagerStateListener {
+ /**
+ * Called when keep dreaming when undocked has changed.
+ *
+ * @param keepDreaming True if the current dream should continue when undocking.
+ */
+ void onKeepDreamingWhenUndockedChanged(boolean keepDreaming);
+ }
}
diff --git a/core/java/android/window/ITaskFragmentOrganizerController.aidl b/core/java/android/window/ITaskFragmentOrganizerController.aidl
index 3250dd8..d25c8a8 100644
--- a/core/java/android/window/ITaskFragmentOrganizerController.aidl
+++ b/core/java/android/window/ITaskFragmentOrganizerController.aidl
@@ -39,13 +39,13 @@
* animations if the transition only contains windows that belong to the organized
* TaskFragments in the given Task.
*/
- void registerRemoteAnimations(in ITaskFragmentOrganizer organizer, int taskId,
+ void registerRemoteAnimations(in ITaskFragmentOrganizer organizer,
in RemoteAnimationDefinition definition);
/**
* Unregisters remote animations per transition type for the organizer.
*/
- void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer, int taskId);
+ void unregisterRemoteAnimations(in ITaskFragmentOrganizer organizer);
/**
* Checks if an activity organized by a {@link android.window.TaskFragmentOrganizer} and
diff --git a/core/java/android/window/TaskFragmentOrganizer.java b/core/java/android/window/TaskFragmentOrganizer.java
index 648541b..ab7d616 100644
--- a/core/java/android/window/TaskFragmentOrganizer.java
+++ b/core/java/android/window/TaskFragmentOrganizer.java
@@ -140,16 +140,13 @@
/**
* Registers remote animations per transition type for the organizer. It will override the
* animations if the transition only contains windows that belong to the organized
- * TaskFragments in the given Task.
- *
- * @param taskId overrides if the transition only contains windows belonging to this Task.
+ * TaskFragments, and at least one of the transition window is embedded (not filling the Task).
* @hide
*/
@CallSuper
- public void registerRemoteAnimations(int taskId,
- @NonNull RemoteAnimationDefinition definition) {
+ public void registerRemoteAnimations(@NonNull RemoteAnimationDefinition definition) {
try {
- getController().registerRemoteAnimations(mInterface, taskId, definition);
+ getController().registerRemoteAnimations(mInterface, definition);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -160,9 +157,9 @@
* @hide
*/
@CallSuper
- public void unregisterRemoteAnimations(int taskId) {
+ public void unregisterRemoteAnimations() {
try {
- getController().unregisterRemoteAnimations(mInterface, taskId);
+ getController().unregisterRemoteAnimations(mInterface);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index bd4f990..004d5d6 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -189,6 +189,8 @@
optional bool is_enhanced_discharge_prediction_personalized = 54;
optional bool is_low_power_standby_active = 55;
optional LowPowerStandbyControllerDumpProto low_power_standby_controller = 56;
+ // The battery level drained by the dream.
+ optional int32 battery_level_drained_while_dreaming = 57;
}
// A com.android.server.power.PowerManagerService.SuspendBlockerImpl object.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index 9d841ea..d7d43aa 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -37,7 +37,6 @@
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
-import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -85,26 +84,20 @@
@Override
public void unregisterOrganizer() {
if (mAnimationController != null) {
- mAnimationController.unregisterAllRemoteAnimations();
+ mAnimationController.unregisterRemoteAnimations();
mAnimationController = null;
}
super.unregisterOrganizer();
}
- /** Overrides the animation if the transition is on the given Task. */
- void startOverrideSplitAnimation(int taskId) {
+ /**
+ * Overrides the animation for transitions of embedded activities organized by this organizer.
+ */
+ void overrideSplitAnimation() {
if (mAnimationController == null) {
mAnimationController = new TaskFragmentAnimationController(this);
}
- mAnimationController.registerRemoteAnimations(taskId);
- }
-
- /** No longer overrides the animation if the transition is on the given Task. */
- @GuardedBy("mLock")
- void stopOverrideSplitAnimation(int taskId) {
- if (mAnimationController != null) {
- mAnimationController.unregisterRemoteAnimations(taskId);
- }
+ mAnimationController.registerRemoteAnimations();
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index d52caaf..c06548a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -193,7 +193,6 @@
continue;
}
updateContainersInTask(wct, taskContainer);
- updateAnimationOverride(taskContainer);
}
// The WCT should be applied and merged to the device state change transition if
// there is one.
@@ -208,9 +207,6 @@
synchronized (mLock) {
mSplitRules.clear();
mSplitRules.addAll(rules);
- for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
- updateAnimationOverride(mTaskContainers.valueAt(i));
- }
}
}
@@ -612,7 +608,6 @@
}
if (taskContainer.isEmpty()) {
// Cleanup the TaskContainer if it becomes empty.
- mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
mTaskContainers.remove(taskContainer.getTaskId());
}
return;
@@ -622,43 +617,7 @@
@GuardedBy("mLock")
private void onTaskContainerInfoChanged(@NonNull TaskContainer taskContainer,
@NonNull Configuration config) {
- final boolean wasInPip = taskContainer.isInPictureInPicture();
- final boolean isInPIp = isInPictureInPicture(config);
-
- // We need to check the animation override when enter/exit PIP or has bounds changed.
- boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
- if (taskContainer.setTaskBounds(config.windowConfiguration.getBounds())
- && !isInPIp) {
- // We don't care the bounds change when it has already entered PIP.
- shouldUpdateAnimationOverride = true;
- }
- if (shouldUpdateAnimationOverride) {
- updateAnimationOverride(taskContainer);
- }
- }
-
- /**
- * Updates if we should override transition animation. We only want to override if the Task
- * bounds is large enough for at least one split rule.
- */
- @GuardedBy("mLock")
- private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
- if (ENABLE_SHELL_TRANSITIONS) {
- // TODO(b/207070762): cleanup with legacy app transition
- // Animation will be handled by WM Shell with Shell transition enabled.
- return;
- }
- if (!taskContainer.isTaskBoundsInitialized()) {
- // We don't know about the Task bounds/windowingMode yet.
- return;
- }
-
- // We only want to override if the TaskContainer may show split.
- if (mayShowSplit(taskContainer)) {
- mPresenter.startOverrideSplitAnimation(taskContainer.getTaskId());
- } else {
- mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
- }
+ taskContainer.setTaskBounds(config.windowConfiguration.getBounds());
}
/** Returns whether the given {@link TaskContainer} may show in split. */
@@ -1283,7 +1242,6 @@
Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
}
}
- updateAnimationOverride(taskContainer);
return container;
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index cb470ba..f494b32 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -139,6 +139,11 @@
super(executor, controller);
mController = controller;
registerOrganizer();
+ if (!SplitController.ENABLE_SHELL_TRANSITIONS) {
+ // TODO(b/207070762): cleanup with legacy app transition
+ // Animation will be handled by WM Shell when Shell transition is enabled.
+ overrideSplitAnimation();
+ }
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
index ee2e139..d7eb9a0 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationController.java
@@ -18,13 +18,10 @@
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
-import android.util.ArraySet;
import android.util.Log;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationDefinition;
@@ -44,8 +41,7 @@
private final TaskFragmentAnimationRunner mRemoteRunner = new TaskFragmentAnimationRunner();
@VisibleForTesting
final RemoteAnimationDefinition mDefinition;
- /** Task Ids that we have registered for remote animation. */
- private final ArraySet<Integer> mRegisterTasks = new ArraySet<>();
+ private boolean mIsRegistered;
TaskFragmentAnimationController(@NonNull TaskFragmentOrganizer organizer) {
mOrganizer = organizer;
@@ -54,39 +50,30 @@
new RemoteAnimationAdapter(mRemoteRunner, 0, 0, true /* changeNeedsSnapshot */);
mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, animationAdapter);
mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, animationAdapter);
- mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_OPEN, animationAdapter);
mDefinition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, animationAdapter);
mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, animationAdapter);
- mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_CLOSE, animationAdapter);
mDefinition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, animationAdapter);
}
- void registerRemoteAnimations(int taskId) {
+ void registerRemoteAnimations() {
if (DEBUG) {
Log.v(TAG, "registerRemoteAnimations");
}
- if (mRegisterTasks.contains(taskId)) {
+ if (mIsRegistered) {
return;
}
- mOrganizer.registerRemoteAnimations(taskId, mDefinition);
- mRegisterTasks.add(taskId);
+ mOrganizer.registerRemoteAnimations(mDefinition);
+ mIsRegistered = true;
}
- void unregisterRemoteAnimations(int taskId) {
+ void unregisterRemoteAnimations() {
if (DEBUG) {
Log.v(TAG, "unregisterRemoteAnimations");
}
- if (!mRegisterTasks.contains(taskId)) {
+ if (!mIsRegistered) {
return;
}
- mOrganizer.unregisterRemoteAnimations(taskId);
- mRegisterTasks.remove(taskId);
- }
-
- void unregisterAllRemoteAnimations() {
- final ArraySet<Integer> tasks = new ArraySet<>(mRegisterTasks);
- for (int taskId : tasks) {
- unregisterRemoteAnimations(taskId);
- }
+ mOrganizer.unregisterRemoteAnimations();
+ mIsRegistered = false;
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 8c416e8..0e13c59 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -20,11 +20,9 @@
import static android.view.RemoteAnimationTarget.MODE_CLOSING;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CHANGE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_FRAGMENT_OPEN;
-import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
import android.animation.Animator;
@@ -169,11 +167,9 @@
switch (transit) {
case TRANSIT_OLD_ACTIVITY_OPEN:
case TRANSIT_OLD_TASK_FRAGMENT_OPEN:
- case TRANSIT_OLD_TASK_OPEN:
return createOpenAnimationAdapters(targets);
case TRANSIT_OLD_ACTIVITY_CLOSE:
case TRANSIT_OLD_TASK_FRAGMENT_CLOSE:
- case TRANSIT_OLD_TASK_CLOSE:
return createCloseAnimationAdapters(targets);
case TRANSIT_OLD_TASK_FRAGMENT_CHANGE:
return createChangeAnimationAdapters(targets);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 79813c7..31aa09c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -18,7 +18,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -26,10 +25,8 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
import android.content.Intent;
import android.content.res.Configuration;
@@ -85,35 +82,20 @@
@Test
public void testUnregisterOrganizer() {
- mOrganizer.startOverrideSplitAnimation(TASK_ID);
- mOrganizer.startOverrideSplitAnimation(TASK_ID + 1);
+ mOrganizer.overrideSplitAnimation();
mOrganizer.unregisterOrganizer();
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID);
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID + 1);
+ verify(mOrganizer).unregisterRemoteAnimations();
}
@Test
- public void testStartOverrideSplitAnimation() {
+ public void testOverrideSplitAnimation() {
assertNull(mOrganizer.mAnimationController);
- mOrganizer.startOverrideSplitAnimation(TASK_ID);
+ mOrganizer.overrideSplitAnimation();
assertNotNull(mOrganizer.mAnimationController);
- verify(mOrganizer).registerRemoteAnimations(TASK_ID,
- mOrganizer.mAnimationController.mDefinition);
- }
-
- @Test
- public void testStopOverrideSplitAnimation() {
- mOrganizer.stopOverrideSplitAnimation(TASK_ID);
-
- verify(mOrganizer, never()).unregisterRemoteAnimations(anyInt());
-
- mOrganizer.startOverrideSplitAnimation(TASK_ID);
- mOrganizer.stopOverrideSplitAnimation(TASK_ID);
-
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID);
+ verify(mOrganizer).registerRemoteAnimations(mOrganizer.mAnimationController.mDefinition);
}
@Test
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
index d31342b..379ea0c 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentAnimationControllerTest.java
@@ -16,11 +16,8 @@
package androidx.window.extensions.embedding;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
import android.platform.test.annotations.Presubmit;
@@ -57,41 +54,31 @@
@Test
public void testRegisterRemoteAnimations() {
- mAnimationController.registerRemoteAnimations(TASK_ID);
+ mAnimationController.registerRemoteAnimations();
- verify(mOrganizer).registerRemoteAnimations(TASK_ID, mAnimationController.mDefinition);
+ verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
- mAnimationController.registerRemoteAnimations(TASK_ID);
+ mAnimationController.registerRemoteAnimations();
// No extra call if it has been registered.
- verify(mOrganizer).registerRemoteAnimations(TASK_ID, mAnimationController.mDefinition);
+ verify(mOrganizer).registerRemoteAnimations(mAnimationController.mDefinition);
}
@Test
public void testUnregisterRemoteAnimations() {
- mAnimationController.unregisterRemoteAnimations(TASK_ID);
+ mAnimationController.unregisterRemoteAnimations();
// No call if it is not registered.
- verify(mOrganizer, never()).unregisterRemoteAnimations(anyInt());
+ verify(mOrganizer, never()).unregisterRemoteAnimations();
- mAnimationController.registerRemoteAnimations(TASK_ID);
- mAnimationController.unregisterRemoteAnimations(TASK_ID);
+ mAnimationController.registerRemoteAnimations();
+ mAnimationController.unregisterRemoteAnimations();
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID);
+ verify(mOrganizer).unregisterRemoteAnimations();
- mAnimationController.unregisterRemoteAnimations(TASK_ID);
+ mAnimationController.unregisterRemoteAnimations();
// No extra call if it has been unregistered.
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID);
- }
-
- @Test
- public void testUnregisterAllRemoteAnimations() {
- mAnimationController.registerRemoteAnimations(TASK_ID);
- mAnimationController.registerRemoteAnimations(TASK_ID + 1);
- mAnimationController.unregisterAllRemoteAnimations();
-
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID);
- verify(mOrganizer).unregisterRemoteAnimations(TASK_ID + 1);
+ verify(mOrganizer).unregisterRemoteAnimations();
}
}
diff --git a/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml b/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
new file mode 100644
index 0000000..416287d
--- /dev/null
+++ b/libs/WindowManager/Shell/res/drawable/decor_caption_menu_background.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<shape android:shape="rectangle"
+ xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/white" />
+ <corners android:radius="20dp" />
+</shape>
diff --git a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
index d9a140b..582a11c 100644
--- a/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/caption_handle_menu.xml
@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
-android:background="@drawable/decor_caption_title">
+android:background="@drawable/decor_caption_menu_background">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/fullscreen_button"
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index ebe5c5e..8369569 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -44,6 +44,8 @@
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import androidx.annotation.Nullable;
+
import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
@@ -71,6 +73,7 @@
private DesktopModeController mDesktopModeController;
private EventReceiver mEventReceiver;
private InputMonitor mInputMonitor;
+ private boolean mTransitionDragActive;
private final SparseArray<CaptionWindowDecoration> mWindowDecorByTaskId = new SparseArray<>();
private final DragStartListenerImpl mDragStartListener = new DragStartListenerImpl();
@@ -91,6 +94,7 @@
mDisplayController = displayController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
+ mTransitionDragActive = false;
}
@Override
@@ -288,7 +292,7 @@
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
if (e.getRawY(dragPointerIdx) <= statusBarHeight
- && windowingMode == WINDOWING_MODE_FREEFORM) {
+ && DesktopModeStatus.isActive(mContext)) {
mDesktopModeController.setDesktopModeActive(false);
}
break;
@@ -306,26 +310,97 @@
@Override
public void onInputEvent(InputEvent event) {
boolean handled = false;
- if (event instanceof MotionEvent
- && ((MotionEvent) event).getActionMasked() == MotionEvent.ACTION_UP) {
+ if (event instanceof MotionEvent) {
handled = true;
- CaptionWindowDecorViewModel.this.handleMotionEvent((MotionEvent) event);
+ CaptionWindowDecorViewModel.this.handleReceivedMotionEvent((MotionEvent) event);
}
finishInputEvent(event, handled);
}
}
- // If any input received is outside of caption bounds, turn off handle menu
- private void handleMotionEvent(MotionEvent ev) {
- int size = mWindowDecorByTaskId.size();
- for (int i = 0; i < size; i++) {
- CaptionWindowDecoration decoration = mWindowDecorByTaskId.valueAt(i);
- if (decoration != null) {
- decoration.closeHandleMenuIfNeeded(ev);
+ /**
+ * Handle MotionEvents relevant to focused task's caption that don't directly touch it
+ * @param ev the {@link MotionEvent} received by {@link EventReceiver}
+ */
+ private void handleReceivedMotionEvent(MotionEvent ev) {
+ if (!DesktopModeStatus.isActive(mContext)) {
+ handleCaptionThroughStatusBar(ev);
+ }
+ handleEventOutsideFocusedCaption(ev);
+ // Prevent status bar from reacting to a caption drag.
+ if (mTransitionDragActive && !DesktopModeStatus.isActive(mContext)) {
+ mInputMonitor.pilferPointers();
+ }
+ }
+
+ // If an UP/CANCEL action is received outside of caption bounds, turn off handle menu
+ private void handleEventOutsideFocusedCaption(MotionEvent ev) {
+ int action = ev.getActionMasked();
+ if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor == null) {
+ return;
+ }
+
+ if (!mTransitionDragActive) {
+ focusedDecor.closeHandleMenuIfNeeded(ev);
}
}
}
+ /**
+ * Perform caption actions if not able to through normal means.
+ * Turn on desktop mode if handle is dragged below status bar.
+ */
+ private void handleCaptionThroughStatusBar(MotionEvent ev) {
+ switch (ev.getActionMasked()) {
+ case MotionEvent.ACTION_DOWN: {
+ // Begin drag through status bar if applicable.
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor != null && !DesktopModeStatus.isActive(mContext)
+ && focusedDecor.checkTouchEventInHandle(ev)) {
+ mTransitionDragActive = true;
+ }
+ break;
+ }
+ case MotionEvent.ACTION_UP: {
+ CaptionWindowDecoration focusedDecor = getFocusedDecor();
+ if (focusedDecor == null) {
+ mTransitionDragActive = false;
+ return;
+ }
+ if (mTransitionDragActive) {
+ mTransitionDragActive = false;
+ int statusBarHeight = mDisplayController
+ .getDisplayLayout(focusedDecor.mTaskInfo.displayId).stableInsets().top;
+ if (ev.getY() > statusBarHeight) {
+ mDesktopModeController.setDesktopModeActive(true);
+ return;
+ }
+ }
+ focusedDecor.checkClickEvent(ev);
+ break;
+ }
+ case MotionEvent.ACTION_CANCEL: {
+ mTransitionDragActive = false;
+ }
+ }
+ }
+
+ @Nullable
+ private CaptionWindowDecoration getFocusedDecor() {
+ int size = mWindowDecorByTaskId.size();
+ CaptionWindowDecoration focusedDecor = null;
+ for (int i = 0; i < size; i++) {
+ CaptionWindowDecoration decor = mWindowDecorByTaskId.valueAt(i);
+ if (decor != null && decor.isFocused()) {
+ focusedDecor = decor;
+ break;
+ }
+ }
+ return focusedDecor;
+ }
+
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index affde30..59576cd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -23,6 +23,7 @@
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
@@ -243,7 +244,7 @@
* Sets the visibility of buttons and color of caption based on desktop mode status
*
*/
- public void setButtonVisibility() {
+ void setButtonVisibility() {
mDesktopActive = DesktopModeStatus.isActive(mContext);
int v = mDesktopActive ? View.VISIBLE : View.GONE;
View caption = mResult.mRootView.findViewById(R.id.caption);
@@ -262,7 +263,7 @@
caption.getBackground().setTint(v == View.VISIBLE ? Color.WHITE : Color.TRANSPARENT);
}
- public boolean isHandleMenuActive() {
+ boolean isHandleMenuActive() {
return mHandleMenu != null;
}
@@ -277,7 +278,7 @@
/**
* Create and display handle menu window
*/
- public void createHandleMenu() {
+ void createHandleMenu() {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
final Resources resources = mDecorWindowContext.getResources();
int x = mRelayoutParams.mCaptionX;
@@ -298,7 +299,7 @@
/**
* Close the handle menu window
*/
- public void closeHandleMenu() {
+ void closeHandleMenu() {
if (!isHandleMenuActive()) return;
mHandleMenu.releaseView();
mHandleMenu = null;
@@ -313,24 +314,85 @@
/**
* Close an open handle menu if input is outside of menu coordinates
* @param ev the tapped point to compare against
- * @return
*/
- public void closeHandleMenuIfNeeded(MotionEvent ev) {
- if (mHandleMenu != null) {
- Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
- .positionInParent;
- final Resources resources = mDecorWindowContext.getResources();
- ev.offsetLocation(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
- ev.offsetLocation(-positionInParent.x, -positionInParent.y);
- int width = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionWidthId);
- int height = loadDimensionPixelSize(resources, mRelayoutParams.mCaptionHeightId);
- if (!(ev.getX() >= 0 && ev.getY() >= 0
- && ev.getX() <= width && ev.getY() <= height)) {
+ void closeHandleMenuIfNeeded(MotionEvent ev) {
+ if (isHandleMenuActive()) {
+ if (!checkEventInCaptionView(ev, R.id.caption)) {
closeHandleMenu();
}
}
}
+ boolean isFocused() {
+ return mTaskInfo.isFocused;
+ }
+
+ /**
+ * Offset the coordinates of a {@link MotionEvent} to be in the same coordinate space as caption
+ * @param ev the {@link MotionEvent} to offset
+ * @return the point of the input in local space
+ */
+ private PointF offsetCaptionLocation(MotionEvent ev) {
+ PointF result = new PointF(ev.getX(), ev.getY());
+ Point positionInParent = mTaskOrganizer.getRunningTaskInfo(mTaskInfo.taskId)
+ .positionInParent;
+ result.offset(-mRelayoutParams.mCaptionX, -mRelayoutParams.mCaptionY);
+ result.offset(-positionInParent.x, -positionInParent.y);
+ return result;
+ }
+
+ /**
+ * Determine if a passed MotionEvent is in a view in caption
+ * @param ev the {@link MotionEvent} to check
+ * @param layoutId the id of the view
+ * @return {@code true} if event is inside the specified view, {@code false} if not
+ */
+ private boolean checkEventInCaptionView(MotionEvent ev, int layoutId) {
+ if (mResult.mRootView == null) return false;
+ PointF inputPoint = offsetCaptionLocation(ev);
+ View view = mResult.mRootView.findViewById(layoutId);
+ return view != null && view.pointInView(inputPoint.x, inputPoint.y, 0);
+ }
+
+ boolean checkTouchEventInHandle(MotionEvent ev) {
+ if (isHandleMenuActive()) return false;
+ return checkEventInCaptionView(ev, R.id.caption_handle);
+ }
+
+ /**
+ * Check a passed MotionEvent if a click has occurred on any button on this caption
+ * Note this should only be called when a regular onClick is not possible
+ * (i.e. the button was clicked through status bar layer)
+ * @param ev the MotionEvent to compare
+ */
+ void checkClickEvent(MotionEvent ev) {
+ if (mResult.mRootView == null) return;
+ View caption = mResult.mRootView.findViewById(R.id.caption);
+ PointF inputPoint = offsetCaptionLocation(ev);
+ if (!isHandleMenuActive()) {
+ View handle = caption.findViewById(R.id.caption_handle);
+ clickIfPointInView(inputPoint, handle);
+ } else {
+ View menu = mHandleMenu.mWindowViewHost.getView();
+ View fullscreen = menu.findViewById(R.id.fullscreen_button);
+ if (clickIfPointInView(inputPoint, fullscreen)) return;
+ View desktop = menu.findViewById(R.id.desktop_button);
+ if (clickIfPointInView(inputPoint, desktop)) return;
+ View split = menu.findViewById(R.id.split_screen_button);
+ if (clickIfPointInView(inputPoint, split)) return;
+ View more = menu.findViewById(R.id.more_button);
+ clickIfPointInView(inputPoint, more);
+ }
+ }
+
+ private boolean clickIfPointInView(PointF inputPoint, View v) {
+ if (v.pointInView(inputPoint.x - v.getLeft(), inputPoint.y, 0)) {
+ mOnCaptionButtonClickListener.onClick(v);
+ return true;
+ }
+ return false;
+ }
+
@Override
public void close() {
closeDragResizeListener();
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 9d7a9e7..3c6f18c 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -91,6 +91,7 @@
"SystemUIAnimationLib",
"SystemUIPluginLib",
"SystemUISharedLib",
+ "SystemUICustomizationLib",
"SystemUI-statsd",
"SettingsLib",
"androidx.core_core-ktx",
@@ -190,6 +191,7 @@
"SystemUIAnimationLib",
"SystemUIPluginLib",
"SystemUISharedLib",
+ "SystemUICustomizationLib",
"SystemUI-statsd",
"SettingsLib",
"androidx.viewpager2_viewpager2",
diff --git a/packages/SystemUI/customization/Android.bp b/packages/SystemUI/customization/Android.bp
new file mode 100644
index 0000000..dc450bb
--- /dev/null
+++ b/packages/SystemUI/customization/Android.bp
@@ -0,0 +1,52 @@
+// Copyright (C) 2017 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 {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+}
+
+android_library {
+ name: "SystemUICustomizationLib",
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ "src/**/*.aidl",
+ ],
+ static_libs: [
+ "PluginCoreLib",
+ "SystemUIAnimationLib",
+ "SystemUIPluginLib",
+ "SystemUIUnfoldLib",
+ "androidx.dynamicanimation_dynamicanimation",
+ "androidx.concurrent_concurrent-futures",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-ktx",
+ "androidx.recyclerview_recyclerview",
+ "kotlinx_coroutines_android",
+ "kotlinx_coroutines",
+ "dagger2",
+ "jsr330",
+ ],
+ resource_dirs: [
+ "res",
+ ],
+ min_sdk_version: "current",
+ plugins: ["dagger2-compiler"],
+ kotlincflags: ["-Xjvm-default=enable"],
+}
diff --git a/packages/SystemUI/customization/AndroidManifest.xml b/packages/SystemUI/customization/AndroidManifest.xml
new file mode 100644
index 0000000..3277bff
--- /dev/null
+++ b/packages/SystemUI/customization/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2017 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.systemui.customization">
+
+</manifest>
diff --git a/packages/SystemUI/shared/res/drawable/clock_default_thumbnail.xml b/packages/SystemUI/customization/res/drawable/clock_default_thumbnail.xml
similarity index 100%
rename from packages/SystemUI/shared/res/drawable/clock_default_thumbnail.xml
rename to packages/SystemUI/customization/res/drawable/clock_default_thumbnail.xml
diff --git a/packages/SystemUI/shared/res/layout/clock_default_large.xml b/packages/SystemUI/customization/res/layout/clock_default_large.xml
similarity index 100%
rename from packages/SystemUI/shared/res/layout/clock_default_large.xml
rename to packages/SystemUI/customization/res/layout/clock_default_large.xml
diff --git a/packages/SystemUI/shared/res/layout/clock_default_small.xml b/packages/SystemUI/customization/res/layout/clock_default_small.xml
similarity index 100%
rename from packages/SystemUI/shared/res/layout/clock_default_small.xml
rename to packages/SystemUI/customization/res/layout/clock_default_small.xml
diff --git a/packages/SystemUI/customization/res/values/attrs.xml b/packages/SystemUI/customization/res/values/attrs.xml
new file mode 100644
index 0000000..f9d66ee
--- /dev/null
+++ b/packages/SystemUI/customization/res/values/attrs.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.
+-->
+
+<!-- Formatting note: terminate all comments with a period, to avoid breaking
+ the documentation output. To suppress comment lines from the documentation
+ output, insert an eat-comment element after the comment lines.
+-->
+
+<resources>
+ <declare-styleable name="AnimatableClockView">
+ <attr name="dozeWeight" format="integer" />
+ <attr name="lockScreenWeight" format="integer" />
+ <attr name="chargeAnimationDelay" format="integer" />
+ </declare-styleable>
+</resources>
diff --git a/packages/SystemUI/shared/res/values/dimens.xml b/packages/SystemUI/customization/res/values/dimens.xml
similarity index 100%
rename from packages/SystemUI/shared/res/values/dimens.xml
rename to packages/SystemUI/customization/res/values/dimens.xml
diff --git a/packages/SystemUI/shared/res/values/donottranslate.xml b/packages/SystemUI/customization/res/values/donottranslate.xml
similarity index 100%
rename from packages/SystemUI/shared/res/values/donottranslate.xml
rename to packages/SystemUI/customization/res/values/donottranslate.xml
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
similarity index 98%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 236aa66..22944b8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -33,9 +33,9 @@
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.TextAnimator
+import com.android.systemui.customization.R
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel.DEBUG
-import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Calendar
import java.util.Locale
@@ -613,7 +613,7 @@
private const val CHARGE_ANIM_DURATION_PHASE_1: Long = 1000
// Constants for the animation
- private val MOVE_INTERPOLATOR = Interpolators.STANDARD
+ private val MOVE_INTERPOLATOR = Interpolators.EMPHASIZED
// Calculate the positions of all of the digits...
// Offset each digit by, say, 0.1
@@ -637,7 +637,10 @@
// How much delay to apply to each subsequent digit. This is measured in terms of "fraction"
// (i.e. a value of 0.1 would cause a digit to wait until fraction had hit 0.1, or 0.2 etc
// before moving).
- private const val MOVE_DIGIT_STEP = 0.1f
+ //
+ // The current specs dictate that each digit should have a 33ms gap between them. The
+ // overall time is 1s right now.
+ private const val MOVE_DIGIT_STEP = 0.033f
// Total available transition time for each digit, taking into account the step. If step is
// 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
similarity index 97%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 5c2c27a..59b4848 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -20,6 +20,7 @@
import android.os.Handler
import android.provider.Settings
import android.util.Log
+import androidx.annotation.OpenForTesting
import com.android.internal.annotations.Keep
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
@@ -27,7 +28,7 @@
import com.android.systemui.plugins.ClockProvider
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.PluginListener
-import com.android.systemui.shared.plugins.PluginManager
+import com.android.systemui.plugins.PluginManager
import org.json.JSONObject
private val TAG = ClockRegistry::class.simpleName
@@ -157,7 +158,8 @@
}
}
- fun getClocks(): List<ClockMetadata> {
+ @OpenForTesting
+ open fun getClocks(): List<ClockMetadata> {
if (!isEnabled) {
return listOf(availableClocks[DEFAULT_CLOCK_ID]!!.metadata)
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
similarity index 99%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 23a7271..8698844 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -23,13 +23,13 @@
import android.view.View
import android.widget.FrameLayout
import androidx.annotation.VisibleForTesting
+import com.android.systemui.customization.R
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.log.LogBuffer
-import com.android.systemui.shared.R
import java.io.PrintWriter
import java.util.Locale
import java.util.TimeZone
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
similarity index 90%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
rename to packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 6627c5d..4c0504b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -17,23 +17,21 @@
import android.content.res.Resources
import android.graphics.drawable.Drawable
import android.view.LayoutInflater
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.customization.R
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProvider
-import com.android.systemui.shared.R
-import javax.inject.Inject
private val TAG = DefaultClockProvider::class.simpleName
const val DEFAULT_CLOCK_NAME = "Default Clock"
const val DEFAULT_CLOCK_ID = "DEFAULT"
/** Provides the default system clock */
-class DefaultClockProvider @Inject constructor(
+class DefaultClockProvider constructor(
val ctx: Context,
val layoutInflater: LayoutInflater,
- @Main val resources: Resources
+ val resources: Resources
) : ClockProvider {
override fun getClocks(): List<ClockMetadata> =
listOf(ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME))
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index 553b86b..0abbb1e 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -23,9 +23,9 @@
-packages/SystemUI/shared/src/com/android/systemui/flags/FlagSerializer.kt
-packages/SystemUI/shared/src/com/android/systemui/flags/FlagSettingsHelper.kt
-packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+-packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
-packages/SystemUI/shared/src/com/android/systemui/shared/rotation/FloatingRotationButtonPositionCalculator.kt
-packages/SystemUI/shared/src/com/android/systemui/shared/system/UncaughtExceptionPreHandlerManager.kt
-packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginManager.java
similarity index 93%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
rename to packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginManager.java
index c89be86..80c64cd 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java
+++ b/packages/SystemUI/plugin_core/src/com/android/systemui/plugins/PluginManager.java
@@ -12,12 +12,10 @@
* permissions and limitations under the License.
*/
-package com.android.systemui.shared.plugins;
+package com.android.systemui.plugins;
import android.text.TextUtils;
-import com.android.systemui.plugins.Plugin;
-import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.annotations.ProvidesInterface;
public interface PluginManager {
diff --git a/packages/SystemUI/res/drawable/ic_ring_volume.xml b/packages/SystemUI/res/drawable/ic_ring_volume.xml
new file mode 100644
index 0000000..343fe5d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ring_volume.xml
@@ -0,0 +1,26 @@
+<!--
+ Copyright (C) 2022 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+ <path
+ android:pathData="M11,7V2H13V7ZM17.6,9.85 L16.2,8.4 19.75,4.85 21.15,6.3ZM6.4,9.85 L2.85,6.3 4.25,4.85 7.8,8.4ZM12,12Q14.95,12 17.812,13.188Q20.675,14.375 22.9,16.75Q23.2,17.05 23.2,17.45Q23.2,17.85 22.9,18.15L20.6,20.4Q20.325,20.675 19.963,20.7Q19.6,20.725 19.3,20.5L16.4,18.3Q16.2,18.15 16.1,17.95Q16,17.75 16,17.5V14.65Q15.05,14.35 14.05,14.175Q13.05,14 12,14Q10.95,14 9.95,14.175Q8.95,14.35 8,14.65V17.5Q8,17.75 7.9,17.95Q7.8,18.15 7.6,18.3L4.7,20.5Q4.4,20.725 4.038,20.7Q3.675,20.675 3.4,20.4L1.1,18.15Q0.8,17.85 0.8,17.45Q0.8,17.05 1.1,16.75Q3.3,14.375 6.175,13.188Q9.05,12 12,12ZM6,15.35Q5.275,15.725 4.6,16.212Q3.925,16.7 3.2,17.3L4.2,18.3L6,16.9ZM18,15.4V16.9L19.8,18.3L20.8,17.35Q20.075,16.7 19.4,16.225Q18.725,15.75 18,15.4ZM6,15.35Q6,15.35 6,15.35Q6,15.35 6,15.35ZM18,15.4Q18,15.4 18,15.4Q18,15.4 18,15.4Z"
+ android:fillColor="?android:attr/colorPrimary"/>
+
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_ring_volume_off.xml b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml
new file mode 100644
index 0000000..74f30d1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_ring_volume_off.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright (C) 2022 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/colorControlNormal">
+<path
+ android:pathData="M0.8,4.2l8.1,8.1c-2.2,0.5 -5.2,1.6 -7.8,4.4c-0.4,0.4 -0.4,1 0,1.4l2.3,2.3c0.3,0.3 0.9,0.4 1.3,0.1l2.9,-2.2C7.8,18.1 8,17.8 8,17.5v-2.9c0.9,-0.3 1.7,-0.5 2.7,-0.6l8.5,8.5l1.4,-1.4L2.2,2.8L0.8,4.2z"
+ android:fillColor="?android:attr/colorPrimary"/>
+ <path
+ android:pathData="M11,2h2v5h-2z"
+ android:fillColor="?android:attr/colorPrimary"/>
+ <path
+ android:pathData="M21.2,6.3l-1.4,-1.4l-3.6,3.6l1.4,1.4C17.6,9.8 21,6.3 21.2,6.3z"
+ android:fillColor="?android:attr/colorPrimary"/>
+ <path
+ android:pathData="M22.9,16.7c-2.8,-3 -6.2,-4.1 -8.4,-4.5l7.2,7.2l1.3,-1.3C23.3,17.7 23.3,17.1 22.9,16.7z"
+ android:fillColor="?android:attr/colorPrimary"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_speaker_mute.xml b/packages/SystemUI/res/drawable/ic_speaker_mute.xml
new file mode 100644
index 0000000..4e402cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_mute.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorPrimary"
+ android:autoMirrored="true">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M19.8,22.6 L16.775,19.575Q16.15,19.975 15.45,20.263Q14.75,20.55 14,20.725V18.675Q14.35,18.55 14.688,18.425Q15.025,18.3 15.325,18.125L12,14.8V20L7,15H3V9H6.2L1.4,4.2L2.8,2.8L21.2,21.2ZM19.6,16.8 L18.15,15.35Q18.575,14.575 18.788,13.725Q19,12.875 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,13.3 20.638,14.525Q20.275,15.75 19.6,16.8ZM16.25,13.45 L14,11.2V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,12.375 16.438,12.738Q16.375,13.1 16.25,13.45ZM12,9.2 L9.4,6.6 12,4ZM10,15.15V12.8L8.2,11H5V13H7.85ZM9.1,11.9Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_speaker_on.xml b/packages/SystemUI/res/drawable/ic_speaker_on.xml
new file mode 100644
index 0000000..2a90e05
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_speaker_on.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ Copyright (C) 2022 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.
+ -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?android:attr/textColorPrimary"
+ android:autoMirrored="true">
+ <path android:fillColor="#FFFFFFFF"
+ android:pathData="M14,20.725V18.675Q16.25,18.025 17.625,16.175Q19,14.325 19,11.975Q19,9.625 17.625,7.775Q16.25,5.925 14,5.275V3.225Q17.1,3.925 19.05,6.362Q21,8.8 21,11.975Q21,15.15 19.05,17.587Q17.1,20.025 14,20.725ZM3,15V9H7L12,4V20L7,15ZM14,16V7.95Q15.175,8.5 15.838,9.6Q16.5,10.7 16.5,12Q16.5,13.275 15.838,14.362Q15.175,15.45 14,16ZM10,8.85 L7.85,11H5V13H7.85L10,15.15ZM7.5,12Z"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6fedb4f..f223eb7 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1489,7 +1489,7 @@
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
- <dimen name="dream_overlay_complication_clock_time_padding">20dp</dimen>
+ <dimen name="dream_overlay_complication_home_controls_padding">28dp</dimen>
<dimen name="dream_overlay_complication_clock_subtitle_text_size">24sp</dimen>
<dimen name="dream_overlay_complication_preview_text_size">36sp</dimen>
<dimen name="dream_overlay_complication_preview_icon_padding">28dp</dimen>
diff --git a/packages/SystemUI/res/xml/qs_header.xml b/packages/SystemUI/res/xml/qs_header.xml
index 8248fcd..982c422 100644
--- a/packages/SystemUI/res/xml/qs_header.xml
+++ b/packages/SystemUI/res/xml/qs_header.xml
@@ -22,50 +22,104 @@
>
<Constraint
+ android:id="@+id/privacy_container">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintEnd_toEndOf="@id/end_guide"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toTopOf="@id/carrier_group"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+ <Constraint
android:id="@+id/clock">
<Layout
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/date"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/privacy_container"
+ app:layout_constraintBottom_toBottomOf="@id/carrier_group"
app:layout_constraintEnd_toStartOf="@id/carrier_group"
app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
+ <Transform
+ android:scaleX="2.57"
+ android:scaleY="2.57"
+ />
</Constraint>
<Constraint
android:id="@+id/date">
<Layout
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/clock"
+ app:layout_constraintEnd_toStartOf="@id/space"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/carrier_group"
app:layout_constraintHorizontal_bias="0"
- />
- <Motion
- app:motionStagger="0.5"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
/>
</Constraint>
<Constraint
android:id="@+id/carrier_group">
- <CustomAttribute
- app:attributeName="alpha"
- app:customFloatValue="1"
- />
+ <Layout
+ app:layout_constraintWidth_min="48dp"
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/large_screen_shade_header_min_height"
+ app:layout_constraintStart_toEndOf="@id/clock"
+ app:layout_constraintTop_toBottomOf="@id/privacy_container"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ />
+ <PropertySet
+ android:alpha="1"
+ />
</Constraint>
<Constraint
- android:id="@+id/privacy_container">
+ android:id="@+id/statusIcons">
<Layout
android:layout_width="wrap_content"
- android:layout_height="48dp"
+ android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintStart_toEndOf="@id/space"
+ app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
+ app:layout_constraintTop_toTopOf="@id/date"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ />
+ </Constraint>
+
+ <Constraint
+ android:id="@+id/batteryRemainingIcon">
+ <Layout
+ android:layout_width="wrap_content"
+ android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
+ app:layout_constraintStart_toEndOf="@id/statusIcons"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="@id/date"
- />
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="spread_inside"
+ />
+ </Constraint>
+
+
+ <Constraint
+ android:id="@id/space">
+ <Layout
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintStart_toEndOf="@id/date"
+ app:layout_constraintEnd_toStartOf="@id/statusIcons"
+ />
</Constraint>
</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/qs_header_new.xml b/packages/SystemUI/res/xml/qs_header_new.xml
deleted file mode 100644
index 982c422..0000000
--- a/packages/SystemUI/res/xml/qs_header_new.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-
-<ConstraintSet
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/qs_header_constraint"
->
-
- <Constraint
- android:id="@+id/privacy_container">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintEnd_toEndOf="@id/end_guide"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintBottom_toTopOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="1"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/clock">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@id/privacy_container"
- app:layout_constraintBottom_toBottomOf="@id/carrier_group"
- app:layout_constraintEnd_toStartOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- <Transform
- android:scaleX="2.57"
- android:scaleY="2.57"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/date">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/space"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/carrier_group"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/carrier_group">
- <Layout
- app:layout_constraintWidth_min="48dp"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/large_screen_shade_header_min_height"
- app:layout_constraintStart_toEndOf="@id/clock"
- app:layout_constraintTop_toBottomOf="@id/privacy_container"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constraintBottom_toTopOf="@id/batteryRemainingIcon"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- <PropertySet
- android:alpha="1"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/statusIcons">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constrainedWidth="true"
- app:layout_constraintStart_toEndOf="@id/space"
- app:layout_constraintEnd_toStartOf="@id/batteryRemainingIcon"
- app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- />
- </Constraint>
-
- <Constraint
- android:id="@+id/batteryRemainingIcon">
- <Layout
- android:layout_width="wrap_content"
- android:layout_height="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constraintHeight_min="@dimen/new_qs_header_non_clickable_element_height"
- app:layout_constraintStart_toEndOf="@id/statusIcons"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toTopOf="@id/date"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintHorizontal_bias="1"
- app:layout_constraintHorizontal_chainStyle="spread_inside"
- />
- </Constraint>
-
-
- <Constraint
- android:id="@id/space">
- <Layout
- android:layout_width="0dp"
- android:layout_height="0dp"
- app:layout_constraintStart_toEndOf="@id/date"
- app:layout_constraintEnd_toStartOf="@id/statusIcons"
- />
- </Constraint>
-</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/res/values/attrs.xml b/packages/SystemUI/shared/res/values/attrs.xml
index 96a5840..f3aeaef 100644
--- a/packages/SystemUI/shared/res/values/attrs.xml
+++ b/packages/SystemUI/shared/res/values/attrs.xml
@@ -20,12 +20,6 @@
-->
<resources>
- <declare-styleable name="AnimatableClockView">
- <attr name="dozeWeight" format="integer" />
- <attr name="lockScreenWeight" format="integer" />
- <attr name="chargeAnimationDelay" format="integer" />
- </declare-styleable>
-
<declare-styleable name="DoubleShadowAttrDeclare">
<attr name="keyShadowBlur" format="dimension" />
<attr name="keyShadowOffsetX" format="dimension" />
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
index f60db2a..71469a3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/keyguard/data/content/KeyguardQuickAffordanceProviderContract.kt
@@ -106,6 +106,8 @@
const val SLOT_ID = "slot_id"
/** String. Unique ID for the selected affordance. */
const val AFFORDANCE_ID = "affordance_id"
+ /** String. Human-readable name for the affordance. */
+ const val AFFORDANCE_NAME = "affordance_name"
}
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
index 9ea4b57..e226d58 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginActionManager.java
@@ -38,6 +38,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException;
import java.util.ArrayList;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 131f728..2f9f5b2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -31,6 +31,7 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager;
import java.io.FileDescriptor;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
index f45887c..f6c75a2 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java
@@ -82,7 +82,8 @@
taskPercent = mDesiredStagePosition != STAGE_POSITION_TOP_OR_LEFT
? mSplitBounds.topTaskPercent
: (1 - (mSplitBounds.topTaskPercent + mSplitBounds.dividerHeightPercent));
- fullscreenTaskHeight = screenHeightPx * taskPercent;
+ // Scale portrait height to that of (actual screen - taskbar inset)
+ fullscreenTaskHeight = (screenHeightPx - taskbarSize) * taskPercent;
canvasScreenRatio = canvasHeight / fullscreenTaskHeight;
} else {
// For landscape, scale the width
diff --git a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
index ad9609f..122c521 100644
--- a/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
+++ b/packages/SystemUI/src/com/android/keyguard/clock/ClockManager.java
@@ -39,8 +39,8 @@
import com.android.systemui.dock.DockManager.DockEventListener;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
import java.util.ArrayList;
import java.util.Collection;
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index b514f60..676979c 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -17,8 +17,10 @@
package com.android.keyguard.dagger;
import android.content.Context;
+import android.content.res.Resources;
import android.os.Handler;
import android.os.UserHandle;
+import android.view.LayoutInflater;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
@@ -26,9 +28,9 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.clocks.ClockRegistry;
import com.android.systemui.shared.clocks.DefaultClockProvider;
-import com.android.systemui.shared.plugins.PluginManager;
import dagger.Module;
import dagger.Provides;
@@ -43,15 +45,16 @@
@Application Context context,
PluginManager pluginManager,
@Main Handler handler,
- DefaultClockProvider defaultClockProvider,
- FeatureFlags featureFlags) {
+ FeatureFlags featureFlags,
+ @Main Resources resources,
+ LayoutInflater layoutInflater) {
return new ClockRegistry(
context,
pluginManager,
handler,
featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
UserHandle.USER_ALL,
- defaultClockProvider,
+ new DefaultClockProvider(context, layoutInflater, resources),
context.getString(R.string.lockscreen_clock_id_fallback));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 51bcd6b..ef16a3a 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -61,6 +61,7 @@
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.DarkIconDispatcher;
import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.VolumeDialogController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.EnhancedEstimates;
@@ -72,7 +73,6 @@
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shade.ShadeController;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper;
diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
index 95f666c..1bb0329 100644
--- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java
@@ -21,8 +21,8 @@
import android.view.View;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.ViewProvider;
-import com.android.systemui.shared.plugins.PluginManager;
/**
* Define an interface or abstract class as follows that includes the
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 83747b4..191ac76 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -115,7 +115,8 @@
setTheme(R.style.Theme_SystemUI);
if (Process.myUserHandle().equals(UserHandle.SYSTEM)) {
- IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+ IntentFilter bootCompletedFilter = new
+ IntentFilter(Intent.ACTION_LOCKED_BOOT_COMPLETED);
bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
// If SF GPU context priority is set to realtime, then SysUI should run at high.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 94f7158..68e1f72 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -129,6 +129,7 @@
private final float mTranslationY;
@VisibleForTesting @ContainerState int mContainerState = STATE_UNKNOWN;
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
+ private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
@@ -497,9 +498,9 @@
.start();
});
}
- OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
- if (dispatcher != null) {
- dispatcher.registerOnBackInvokedCallback(
+ mOnBackInvokedDispatcher = findOnBackInvokedDispatcher();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
}
}
@@ -600,11 +601,11 @@
@Override
public void onDetachedFromWindow() {
- OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
- if (dispatcher != null) {
- findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
- }
super.onDetachedFromWindow();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback);
+ mOnBackInvokedDispatcher = null;
+ }
mWakefulnessLifecycle.removeObserver(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 76cd3f4..e43c0b9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -35,6 +35,8 @@
import android.view.inputmethod.InputMethodManager;
import android.widget.ImeAwareEditText;
import android.widget.TextView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
@@ -58,6 +60,8 @@
private ViewGroup mAuthCredentialHeader;
private ViewGroup mAuthCredentialInput;
private int mBottomInset = 0;
+ private OnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
public AuthCredentialPasswordView(Context context,
AttributeSet attrs) {
@@ -79,8 +83,7 @@
return false;
}
if (event.getAction() == KeyEvent.ACTION_UP) {
- mContainerView.sendEarlyUserCanceled();
- mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ onBackInvoked();
}
return true;
});
@@ -88,6 +91,11 @@
setOnApplyWindowInsetsListener(this);
}
+ private void onBackInvoked() {
+ mContainerView.sendEarlyUserCanceled();
+ mContainerView.animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ }
+
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -100,6 +108,12 @@
mPasswordField.requestFocus();
mPasswordField.scheduleShowSoftInput();
+
+ mOnBackInvokedDispatcher = findOnBackInvokedDispatcher();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
+ }
}
@Override
@@ -137,6 +151,15 @@
}
@Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ if (mOnBackInvokedDispatcher != null) {
+ mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback);
+ mOnBackInvokedDispatcher = null;
+ }
+ }
+
+ @Override
protected void onCredentialVerified(@NonNull VerifyCredentialResponse response,
int timeoutMs) {
super.onCredentialVerified(response, timeoutMs);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
index c4723e8..5c905df 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerProxy.java
@@ -29,7 +29,7 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.FalsingPlugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.util.DeviceConfigProxy;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
index e8d7e46..f8bd1e7 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java
@@ -27,7 +27,7 @@
import com.android.systemui.plugins.DozeServicePlugin;
import com.android.systemui.plugins.DozeServicePlugin.RequestDoze;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
new file mode 100644
index 0000000..12ceedd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTransitionListener.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 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.doze
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.CallbackController
+import javax.inject.Inject
+
+/** Receives doze transition events, and passes those events to registered callbacks. */
+@SysUISingleton
+class DozeTransitionListener @Inject constructor() :
+ DozeMachine.Part, CallbackController<DozeTransitionCallback> {
+ val callbacks = mutableSetOf<DozeTransitionCallback>()
+ var oldState = DozeMachine.State.UNINITIALIZED
+ var newState = DozeMachine.State.UNINITIALIZED
+
+ override fun transitionTo(oldState: DozeMachine.State, newState: DozeMachine.State) {
+ this.oldState = oldState
+ this.newState = newState
+ callbacks.forEach { it.onDozeTransition(oldState, newState) }
+ }
+
+ override fun addCallback(callback: DozeTransitionCallback) {
+ callbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: DozeTransitionCallback) {
+ callbacks.remove(callback)
+ }
+}
+
+interface DozeTransitionCallback {
+ fun onDozeTransition(oldState: DozeMachine.State, newState: DozeMachine.State)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
index 98cd2d7..069344f 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/dagger/DozeModule.java
@@ -35,6 +35,7 @@
import com.android.systemui.doze.DozeSensors;
import com.android.systemui.doze.DozeSuppressor;
import com.android.systemui.doze.DozeSuspendScreenStatePreventingAdapter;
+import com.android.systemui.doze.DozeTransitionListener;
import com.android.systemui.doze.DozeTriggers;
import com.android.systemui.doze.DozeUi;
import com.android.systemui.doze.DozeWallpaperState;
@@ -83,7 +84,7 @@
DozeUi dozeUi, DozeScreenState dozeScreenState,
DozeScreenBrightness dozeScreenBrightness, DozeWallpaperState dozeWallpaperState,
DozeDockHandler dozeDockHandler, DozeAuthRemover dozeAuthRemover,
- DozeSuppressor dozeSuppressor) {
+ DozeSuppressor dozeSuppressor, DozeTransitionListener dozeTransitionListener) {
return new DozeMachine.Part[]{
dozePauser,
dozeFalsingManagerAdapter,
@@ -94,7 +95,8 @@
dozeWallpaperState,
dozeDockHandler,
dozeAuthRemover,
- dozeSuppressor
+ dozeSuppressor,
+ dozeTransitionListener
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
index 69b85b5..a514c47 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
@@ -81,7 +81,8 @@
ComplicationLayoutParams.DIRECTION_UP,
DREAM_HOME_CONTROLS_CHIP_COMPLICATION_WEIGHT,
// Add margin to the bottom of home controls to horizontally align with smartspace.
- res.getDimensionPixelSize(R.dimen.dream_overlay_complication_clock_time_padding));
+ res.getDimensionPixelSize(
+ R.dimen.dream_overlay_complication_home_controls_padding));
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
index d1a14a1..5dae0a2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebug.java
@@ -86,7 +86,7 @@
new ServerFlagReader.ChangeListener() {
@Override
public void onChange() {
- mRestarter.restartSystemUI();
+ mRestarter.restart();
}
};
@@ -326,7 +326,9 @@
Log.i(TAG, "SystemUI Restart Suppressed");
return;
}
- mRestarter.restartSystemUI();
+ Log.i(TAG, "Restarting SystemUI");
+ // SysUI starts back when up exited. Is there a better way to do this?
+ System.exit(0);
}
private void restartAndroid(boolean requestSuppress) {
@@ -334,7 +336,7 @@
Log.i(TAG, "Android Restart Suppressed");
return;
}
- mRestarter.restartAndroid();
+ mRestarter.restart();
}
void setBooleanFlagInternal(Flag<?> flag, boolean value) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt
index 069e612..3d9f627 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsDebugRestarter.kt
@@ -28,8 +28,6 @@
private val systemExitRestarter: SystemExitRestarter,
) : Restarter {
- private var androidRestartRequested = false
-
val observer =
object : WakefulnessLifecycle.Observer {
override fun onFinishedGoingToSleep() {
@@ -38,18 +36,8 @@
}
}
- override fun restartSystemUI() {
- Log.d(FeatureFlagsDebug.TAG, "SystemUI Restart requested. Restarting on next screen off.")
- scheduleRestart()
- }
-
- override fun restartAndroid() {
- Log.d(FeatureFlagsDebug.TAG, "Android Restart requested. Restarting on next screen off.")
- androidRestartRequested = true
- scheduleRestart()
- }
-
- fun scheduleRestart() {
+ override fun restart() {
+ Log.d(FeatureFlagsDebug.TAG, "Restart requested. Restarting on next screen off.")
if (wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP) {
restartNow()
} else {
@@ -58,10 +46,6 @@
}
private fun restartNow() {
- if (androidRestartRequested) {
- systemExitRestarter.restartAndroid()
- } else {
- systemExitRestarter.restartSystemUI()
- }
+ systemExitRestarter.restart()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
index 8bddacc..3c83682 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsRelease.java
@@ -61,7 +61,7 @@
new ServerFlagReader.ChangeListener() {
@Override
public void onChange() {
- mRestarter.restartSystemUI();
+ mRestarter.restart();
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt
index 7ff3876..a3f0f66 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsReleaseRestarter.kt
@@ -34,48 +34,35 @@
@Background private val bgExecutor: DelayableExecutor,
private val systemExitRestarter: SystemExitRestarter
) : Restarter {
- var listenersAdded = false
+ var shouldRestart = false
var pendingRestart: Runnable? = null
- var androidRestartRequested = false
val observer =
object : WakefulnessLifecycle.Observer {
override fun onFinishedGoingToSleep() {
- scheduleRestart()
+ maybeScheduleRestart()
}
}
val batteryCallback =
object : BatteryController.BatteryStateChangeCallback {
override fun onBatteryLevelChanged(level: Int, pluggedIn: Boolean, charging: Boolean) {
- scheduleRestart()
+ maybeScheduleRestart()
}
}
- override fun restartSystemUI() {
- Log.d(
- FeatureFlagsDebug.TAG,
- "SystemUI Restart requested. Restarting when plugged in and idle."
- )
- scheduleRestart()
- }
-
- override fun restartAndroid() {
- Log.d(
- FeatureFlagsDebug.TAG,
- "Android Restart requested. Restarting when plugged in and idle."
- )
- androidRestartRequested = true
- scheduleRestart()
- }
-
- private fun scheduleRestart() {
- // Don't bother adding listeners twice.
- if (!listenersAdded) {
- listenersAdded = true
+ override fun restart() {
+ Log.d(FeatureFlagsDebug.TAG, "Restart requested. Restarting when plugged in and idle.")
+ if (!shouldRestart) {
+ // Don't bother scheduling twice.
+ shouldRestart = true
wakefulnessLifecycle.addObserver(observer)
batteryController.addCallback(batteryCallback)
+ maybeScheduleRestart()
}
+ }
+
+ private fun maybeScheduleRestart() {
if (
wakefulnessLifecycle.wakefulness == WAKEFULNESS_ASLEEP && batteryController.isPluggedIn
) {
@@ -90,10 +77,6 @@
private fun restartNow() {
Log.d(FeatureFlagsRelease.TAG, "Restarting due to systemui flag change")
- if (androidRestartRequested) {
- systemExitRestarter.restartAndroid()
- } else {
- systemExitRestarter.restartSystemUI()
- }
+ systemExitRestarter.restart()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 81ee578..7558b41 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -75,7 +75,11 @@
@JvmField
val NOTIFICATION_DISMISSAL_FADE =
unreleasedFlag(113, "notification_dismissal_fade", teamfood = true)
- val STABILITY_INDEX_FIX = unreleasedFlag(114, "stability_index_fix", teamfood = true)
+
+ // TODO(b/259558771): Tracking Bug
+ val STABILITY_INDEX_FIX = releasedFlag(114, "stability_index_fix")
+
+ // TODO(b/259559750): Tracking Bug
val SEMI_STABLE_SORT = unreleasedFlag(115, "semi_stable_sort", teamfood = true)
@JvmField
@@ -90,6 +94,10 @@
// TODO(b/257506350): Tracking Bug
val FSI_CHROME = unreleasedFlag(117, "fsi_chrome")
+ @JvmField
+ val SIMPLIFIED_APPEAR_FRACTION =
+ unreleasedFlag(259395680, "simplified_appear_fraction", teamfood = true)
+
// TODO(b/257315550): Tracking Bug
val NO_HUN_FOR_OLD_WHEN = unreleasedFlag(118, "no_hun_for_old_when")
@@ -153,8 +161,8 @@
unreleasedFlag(216, "customizable_lock_screen_quick_affordances", teamfood = false)
/** Shows chipbar UI whenever the device is unlocked by ActiveUnlock (watch). */
- // TODO(b/240196500): Tracking Bug
- @JvmField val ACTIVE_UNLOCK_CHIPBAR = unreleasedFlag(217, "active_unlock_chipbar")
+ // TODO(b/256513609): Tracking Bug
+ @JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@@ -181,9 +189,6 @@
"qs_user_detail_shortcut"
)
- // TODO(b/254512747): Tracking Bug
- val NEW_HEADER = releasedFlag(505, "new_header")
-
// TODO(b/254512383): Tracking Bug
@JvmField
val FULL_SCREEN_USER_SWITCHER =
@@ -414,4 +419,7 @@
@JvmField val UDFPS_NEW_TOUCH_DETECTION = unreleasedFlag(2200, "udfps_new_touch_detection")
@JvmField val UDFPS_ELLIPSE_DEBUG_UI = unreleasedFlag(2201, "udfps_ellipse_debug")
@JvmField val UDFPS_ELLIPSE_DETECTION = unreleasedFlag(2202, "udfps_ellipse_detection")
+
+ // TODO(b259590361): Tracking bug
+ val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
index ce8b821..8f095a2 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Restarter.kt
@@ -16,7 +16,5 @@
package com.android.systemui.flags
interface Restarter {
- fun restartSystemUI()
-
- fun restartAndroid()
-}
+ fun restart()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt b/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
index 89daa64..f1b1be4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/SystemExitRestarter.kt
@@ -16,19 +16,10 @@
package com.android.systemui.flags
-import com.android.internal.statusbar.IStatusBarService
import javax.inject.Inject
-class SystemExitRestarter
-@Inject
-constructor(
- private val barService: IStatusBarService,
-) : Restarter {
- override fun restartAndroid() {
- barService.restart()
- }
-
- override fun restartSystemUI() {
+class SystemExitRestarter @Inject constructor() : Restarter {
+ override fun restart() {
System.exit(0)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
index 1f1ed00..bfc60c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProvider.kt
@@ -199,16 +199,19 @@
arrayOf(
Contract.SelectionTable.Columns.SLOT_ID,
Contract.SelectionTable.Columns.AFFORDANCE_ID,
+ Contract.SelectionTable.Columns.AFFORDANCE_NAME,
)
)
.apply {
- val affordanceIdsBySlotId = interactor.getSelections()
- affordanceIdsBySlotId.entries.forEach { (slotId, affordanceIds) ->
- affordanceIds.forEach { affordanceId ->
+ val affordanceRepresentationsBySlotId = interactor.getSelections()
+ affordanceRepresentationsBySlotId.entries.forEach {
+ (slotId, affordanceRepresentations) ->
+ affordanceRepresentations.forEach { affordanceRepresentation ->
addRow(
arrayOf(
slotId,
- affordanceId,
+ affordanceRepresentation.id,
+ affordanceRepresentation.name,
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 250317b..8403fe6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -137,6 +137,7 @@
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
@@ -851,6 +852,7 @@
@Override
public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
mOccludeAnimationPlaying = true;
+ mScrimControllerLazy.get().setOccludeAnimationPlaying(true);
}
@Override
@@ -861,6 +863,7 @@
// Ensure keyguard state is set correctly if we're cancelled.
mCentralSurfaces.updateIsKeyguard();
+ mScrimControllerLazy.get().setOccludeAnimationPlaying(false);
}
@Override
@@ -874,6 +877,7 @@
// Hide the keyguard now that we're done launching the occluding activity over
// it.
mCentralSurfaces.updateIsKeyguard();
+ mScrimControllerLazy.get().setOccludeAnimationPlaying(false);
mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
}
@@ -895,25 +899,32 @@
@NonNull
@Override
public LaunchAnimator.State createAnimatorState() {
- final int width = getLaunchContainer().getWidth();
- final int height = getLaunchContainer().getHeight();
-
- final float initialHeight = height / 3f;
- final float initialWidth = width / 3f;
+ final int fullWidth = getLaunchContainer().getWidth();
+ final int fullHeight = getLaunchContainer().getHeight();
if (mUpdateMonitor.isSecureCameraLaunchedOverKeyguard()) {
+ final float initialHeight = fullHeight / 3f;
+ final float initialWidth = fullWidth / 3f;
+
// Start the animation near the power button, at one-third size, since the
// camera was launched from the power button.
return new LaunchAnimator.State(
(int) (mPowerButtonY - initialHeight / 2f) /* top */,
(int) (mPowerButtonY + initialHeight / 2f) /* bottom */,
- (int) (width - initialWidth) /* left */,
- width /* right */,
+ (int) (fullWidth - initialWidth) /* left */,
+ fullWidth /* right */,
mWindowCornerRadius, mWindowCornerRadius);
} else {
- // Start the animation in the center of the screen, scaled down.
+ final float initialHeight = fullHeight / 2f;
+ final float initialWidth = fullWidth / 2f;
+
+ // Start the animation in the center of the screen, scaled down to half
+ // size.
return new LaunchAnimator.State(
- height / 2, height / 2, width / 2, width / 2,
+ (int) (fullHeight - initialHeight) / 2,
+ (int) (initialHeight + (fullHeight - initialHeight) / 2),
+ (int) (fullWidth - initialWidth) / 2,
+ (int) (initialWidth + (fullWidth - initialWidth) / 2),
mWindowCornerRadius, mWindowCornerRadius);
}
}
@@ -1131,6 +1142,7 @@
private ScreenOnCoordinator mScreenOnCoordinator;
private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
+ private Lazy<ScrimController> mScrimControllerLazy;
/**
* Injected constructor. See {@link KeyguardModule}.
@@ -1161,7 +1173,8 @@
DreamOverlayStateController dreamOverlayStateController,
Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+ Lazy<ScrimController> scrimControllerLazy) {
mContext = context;
mUserTracker = userTracker;
mFalsingCollector = falsingCollector;
@@ -1206,6 +1219,7 @@
mDreamOverlayStateController = dreamOverlayStateController;
mActivityLaunchAnimator = activityLaunchAnimator;
+ mScrimControllerLazy = scrimControllerLazy;
mPowerButtonY = context.getResources().getDimensionPixelSize(
R.dimen.physical_power_button_center_screen_location_y);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index ef3c443..47ef0fa 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.util.DeviceConfigProxy;
@@ -113,7 +114,8 @@
DreamOverlayStateController dreamOverlayStateController,
Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
- Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
+ Lazy<ScrimController> scrimControllerLazy) {
return new KeyguardViewMediator(
context,
userTracker,
@@ -142,7 +144,8 @@
dreamOverlayStateController,
shadeController,
notificationShadeWindowController,
- activityLaunchAnimator);
+ activityLaunchAnimator,
+ scrimControllerLazy);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 9a90fe7..783f752 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -23,10 +23,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer
import javax.inject.Inject
-import kotlinx.coroutines.channels.BufferOverflow
-import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
/** Encapsulates app state for the lock screen primary and alternate bouncer. */
@@ -71,12 +68,8 @@
private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
/** Determines if user is already unlocked */
val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
- private val _showMessage =
- MutableSharedFlow<BouncerShowMessageModel?>(
- replay = 1,
- onBufferOverflow = BufferOverflow.DROP_OLDEST
- )
- val showMessage = _showMessage.asSharedFlow()
+ private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
+ val showMessage = _showMessage.asStateFlow()
private val _resourceUpdateRequests = MutableStateFlow(false)
val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
val bouncerPromptReason: Int
@@ -125,7 +118,7 @@
}
fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
- _showMessage.tryEmit(bouncerShowMessageModel)
+ _showMessage.value = bouncerShowMessageModel
}
fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 9d5d8bb..796f2b4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -23,9 +23,14 @@
import com.android.systemui.common.shared.model.Position
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.doze.DozeHost
+import com.android.systemui.doze.DozeMachine
+import com.android.systemui.doze.DozeTransitionCallback
+import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
@@ -108,6 +113,9 @@
*/
val dozeAmount: Flow<Float>
+ /** Doze state information, as it transitions */
+ val dozeTransitionModel: Flow<DozeTransitionModel>
+
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState>
@@ -154,6 +162,7 @@
biometricUnlockController: BiometricUnlockController,
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val dozeTransitionListener: DozeTransitionListener,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -286,6 +295,37 @@
awaitClose { statusBarStateController.removeCallback(callback) }
}
+ override val dozeTransitionModel: Flow<DozeTransitionModel> = conflatedCallbackFlow {
+ val callback =
+ object : DozeTransitionCallback {
+ override fun onDozeTransition(
+ oldState: DozeMachine.State,
+ newState: DozeMachine.State
+ ) {
+ trySendWithFailureLogging(
+ DozeTransitionModel(
+ from = dozeMachineStateToModel(oldState),
+ to = dozeMachineStateToModel(newState),
+ ),
+ TAG,
+ "doze transition model"
+ )
+ }
+ }
+
+ dozeTransitionListener.addCallback(callback)
+ trySendWithFailureLogging(
+ DozeTransitionModel(
+ from = dozeMachineStateToModel(dozeTransitionListener.oldState),
+ to = dozeMachineStateToModel(dozeTransitionListener.newState),
+ ),
+ TAG,
+ "initial doze transition model"
+ )
+
+ awaitClose { dozeTransitionListener.removeCallback(callback) }
+ }
+
override fun isKeyguardShowing(): Boolean {
return keyguardStateController.isShowing
}
@@ -407,6 +447,25 @@
}
}
+ private fun dozeMachineStateToModel(state: DozeMachine.State): DozeStateModel {
+ return when (state) {
+ DozeMachine.State.UNINITIALIZED -> DozeStateModel.UNINITIALIZED
+ DozeMachine.State.INITIALIZED -> DozeStateModel.INITIALIZED
+ DozeMachine.State.DOZE -> DozeStateModel.DOZE
+ DozeMachine.State.DOZE_SUSPEND_TRIGGERS -> DozeStateModel.DOZE_SUSPEND_TRIGGERS
+ DozeMachine.State.DOZE_AOD -> DozeStateModel.DOZE_AOD
+ DozeMachine.State.DOZE_REQUEST_PULSE -> DozeStateModel.DOZE_REQUEST_PULSE
+ DozeMachine.State.DOZE_PULSING -> DozeStateModel.DOZE_PULSING
+ DozeMachine.State.DOZE_PULSING_BRIGHT -> DozeStateModel.DOZE_PULSING_BRIGHT
+ DozeMachine.State.DOZE_PULSE_DONE -> DozeStateModel.DOZE_PULSE_DONE
+ DozeMachine.State.FINISH -> DozeStateModel.FINISH
+ DozeMachine.State.DOZE_AOD_PAUSED -> DozeStateModel.DOZE_AOD_PAUSED
+ DozeMachine.State.DOZE_AOD_PAUSING -> DozeStateModel.DOZE_AOD_PAUSING
+ DozeMachine.State.DOZE_AOD_DOCKED -> DozeStateModel.DOZE_AOD_DOCKED
+ else -> throw IllegalArgumentException("Invalid DozeMachine.State: state")
+ }
+ }
+
companion object {
private const val TAG = "KeyguardRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index e5521c7..2dbacd5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -21,10 +21,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isSleepingOrStartingToSleep
-import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -39,27 +38,24 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) : TransitionInteractor("AOD<->LOCKSCREEN") {
+) : TransitionInteractor(AodLockscreenTransitionInteractor::class.simpleName!!) {
override fun start() {
+ listenForTransitionToAodFromLockscreen()
+ listenForTransitionToLockscreenFromAod()
+ }
+
+ private fun listenForTransitionToAodFromLockscreen() {
scope.launch {
- /*
- * Listening to the startedKeyguardTransitionStep (last started step) allows this code
- * to interrupt an active transition, as long as they were either going to LOCKSCREEN or
- * AOD state. One example is when the user presses the power button in the middle of an
- * active transition.
- */
- keyguardInteractor.wakefulnessState
+ keyguardInteractor
+ .dozeTransitionTo(DozeStateModel.DOZE_AOD)
.sample(
keyguardTransitionInteractor.startedKeyguardTransitionStep,
{ a, b -> Pair(a, b) }
)
.collect { pair ->
- val (wakefulnessState, lastStartedStep) = pair
- if (
- isSleepingOrStartingToSleep(wakefulnessState) &&
- lastStartedStep.to == KeyguardState.LOCKSCREEN
- ) {
+ val (dozeToAod, lastStartedStep) = pair
+ if (lastStartedStep.to == KeyguardState.LOCKSCREEN) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
@@ -68,10 +64,22 @@
getAnimator(),
)
)
- } else if (
- isWakingOrStartingToWake(wakefulnessState) &&
- lastStartedStep.to == KeyguardState.AOD
- ) {
+ }
+ }
+ }
+ }
+
+ private fun listenForTransitionToLockscreenFromAod() {
+ scope.launch {
+ keyguardInteractor
+ .dozeTransitionTo(DozeStateModel.FINISH)
+ .sample(
+ keyguardTransitionInteractor.startedKeyguardTransitionStep,
+ { a, b -> Pair(a, b) }
+ )
+ .collect { pair ->
+ val (dozeToAod, lastStartedStep) = pair
+ if (lastStartedStep.to == KeyguardState.AOD) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
name,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
index 7e01db3..2a220fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
@@ -40,7 +40,7 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) : TransitionInteractor("AOD->GONE") {
+) : TransitionInteractor(AodToGoneTransitionInteractor::class.simpleName!!) {
private val wakeAndUnlockModes =
setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
index dd29673..056c44d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerToGoneTransitionInteractor.kt
@@ -40,7 +40,7 @@
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
-) : TransitionInteractor("BOUNCER->GONE") {
+) : TransitionInteractor(BouncerToGoneTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
index c44cda4..9cbf9ea 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingLockscreenTransitionInteractor.kt
@@ -21,12 +21,14 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@SysUISingleton
@@ -37,32 +39,43 @@
private val keyguardInteractor: KeyguardInteractor,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
-) : TransitionInteractor("DREAMING<->LOCKSCREEN") {
+) : TransitionInteractor(DreamingLockscreenTransitionInteractor::class.simpleName!!) {
override fun start() {
scope.launch {
keyguardInteractor.isDreaming
- .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
- .collect { pair ->
- val (isDreaming, keyguardState) = pair
- if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.LOCKSCREEN,
- KeyguardState.DREAMING,
- getAnimator(),
+ .sample(
+ combine(
+ keyguardInteractor.dozeTransitionModel,
+ keyguardTransitionInteractor.finishedKeyguardState
+ ) { a, b -> Pair(a, b) },
+ { a, bc -> Triple(a, bc.first, bc.second) }
+ )
+ .collect { triple ->
+ val (isDreaming, dozeTransitionModel, keyguardState) = triple
+ // Dozing/AOD and dreaming have overlapping events. If the state remains in
+ // FINISH, it means that doze mode is not running and DREAMING is ok to
+ // commence.
+ if (dozeTransitionModel.to == DozeStateModel.FINISH) {
+ if (isDreaming && keyguardState == KeyguardState.LOCKSCREEN) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.DREAMING,
+ getAnimator(),
+ )
)
- )
- } else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
- keyguardTransitionRepository.startTransition(
- TransitionInfo(
- name,
- KeyguardState.DREAMING,
- KeyguardState.LOCKSCREEN,
- getAnimator(),
+ } else if (!isDreaming && keyguardState == KeyguardState.DREAMING) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.DREAMING,
+ KeyguardState.LOCKSCREEN,
+ getAnimator(),
+ )
)
- )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 5a1c702..7cfd117 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -20,10 +20,13 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
/**
* Encapsulates business-logic related to the keyguard but not to a more specific part within it.
@@ -41,6 +44,8 @@
val dozeAmount: Flow<Float> = repository.dozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
+ /** Doze transition information. */
+ val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel
/**
* Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true,
* but not vice-versa.
@@ -62,6 +67,10 @@
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> {
+ return dozeTransitionModel.filter { it.to == state }
+ }
+
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 45eb6f5..c8216c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -189,12 +189,18 @@
}
/** Returns affordance IDs indexed by slot ID, for all known slots. */
- fun getSelections(): Map<String, List<String>> {
+ fun getSelections(): Map<String, List<KeyguardQuickAffordancePickerRepresentation>> {
check(isUsingRepository)
+ val slots = repository.get().getSlotPickerRepresentations()
val selections = repository.get().getSelections()
- return repository.get().getSlotPickerRepresentations().associate { slotRepresentation ->
- slotRepresentation.id to (selections[slotRepresentation.id] ?: emptyList())
+ val affordanceById =
+ getAffordancePickerRepresentations().associateBy { affordance -> affordance.id }
+ return slots.associate { slot ->
+ slot.id to
+ (selections[slot.id] ?: emptyList()).mapNotNull { affordanceId ->
+ affordanceById[affordanceId]
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index cca2d56..3bb8241 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -44,7 +44,7 @@
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
-) : TransitionInteractor("LOCKSCREEN<->BOUNCER") {
+) : TransitionInteractor(LockscreenBouncerTransitionInteractor::class.simpleName!!) {
private var transitionId: UUID? = null
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 910cdf2..3b31dcf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -271,6 +271,11 @@
repository.setKeyguardAuthenticated(null)
}
+ /** Notifies that the message was shown. */
+ fun onMessageShown() {
+ repository.setShowMessage(null)
+ }
+
/** Notify that view visibility has changed. */
fun notifyBouncerVisibilityHasChanged(visibility: Int) {
primaryBouncerCallbackInteractor.dispatchVisibilityChanged(visibility)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeStateModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeStateModel.kt
new file mode 100644
index 0000000..7039188
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeStateModel.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 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.keyguard.shared.model
+
+/** Model device doze states. */
+enum class DozeStateModel {
+ /** Default state. Transition to INITIALIZED to get Doze going. */
+ UNINITIALIZED,
+ /** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
+ INITIALIZED,
+ /** Regular doze. Device is asleep and listening for pulse triggers. */
+ DOZE,
+ /** Deep doze. Device is asleep and is not listening for pulse triggers. */
+ DOZE_SUSPEND_TRIGGERS,
+ /** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
+ DOZE_AOD,
+ /** Pulse has been requested. Device is awake and preparing UI */
+ DOZE_REQUEST_PULSE,
+ /** Pulse is showing. Device is awake and showing UI. */
+ DOZE_PULSING,
+ /** Pulse is showing with bright wallpaper. Device is awake and showing UI. */
+ DOZE_PULSING_BRIGHT,
+ /** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
+ DOZE_PULSE_DONE,
+ /** Doze is done. DozeService is finished. */
+ FINISH,
+ /** AOD, but the display is temporarily off. */
+ DOZE_AOD_PAUSED,
+ /** AOD, prox is near, transitions to DOZE_AOD_PAUSED after a timeout. */
+ DOZE_AOD_PAUSING,
+ /** Always-on doze. Device is awake, showing docking UI and listening for pulse triggers. */
+ DOZE_AOD_DOCKED
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeTransitionModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeTransitionModel.kt
new file mode 100644
index 0000000..e96ace2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/DozeTransitionModel.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 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.keyguard.shared.model
+
+/** Doze transition information. */
+data class DozeTransitionModel(
+ val from: DozeStateModel = DozeStateModel.UNINITIALIZED,
+ val to: DozeStateModel = DozeStateModel.UNINITIALIZED,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 7739a45..3c927ee 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -32,7 +32,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -182,6 +181,7 @@
launch {
viewModel.bouncerShowMessage.collect {
hostViewController.showMessage(it.message, it.colorStateList)
+ viewModel.onMessageShown()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index 526ae74..503c8ba 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -86,6 +86,11 @@
interactor.notifyKeyguardAuthenticatedHandled()
}
+ /** Notifies that the message was shown. */
+ fun onMessageShown() {
+ interactor.onMessageShown()
+ }
+
/** Observe whether back button is enabled. */
fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> {
return interactor.isBackButtonEnabled.map { enabled ->
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index ed1e018..cb0f3e2 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -69,9 +69,9 @@
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.NavigationEdgeBackPlugin;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputChannelCompat;
import com.android.systemui.shared.system.QuickStepContract;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
index 0b565ea..e6575d5a 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java
@@ -18,7 +18,6 @@
import com.android.systemui.Dependency;
import com.android.systemui.plugins.PluginDependency.DependencyProvider;
-import com.android.systemui.shared.plugins.PluginManager;
import javax.inject.Inject;
import javax.inject.Singleton;
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
index 638f81b..146633d 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginsModule.java
@@ -27,7 +27,6 @@
import com.android.systemui.shared.plugins.PluginActionManager;
import com.android.systemui.shared.plugins.PluginEnabler;
import com.android.systemui.shared.plugins.PluginInstance;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginManagerImpl;
import com.android.systemui.shared.plugins.PluginPrefs;
import com.android.systemui.shared.system.UncaughtExceptionPreHandlerManager;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index f37d668..6240c10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -41,6 +41,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.nano.SystemUIProtoDump;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -53,7 +54,6 @@
import com.android.systemui.qs.nano.QsTileState;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
diff --git a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
index 2d1d8b7..d33d113 100644
--- a/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/sensorprivacy/SensorUseStartedActivity.kt
@@ -29,6 +29,8 @@
import android.hardware.SensorPrivacyManager.Sources.DIALOG
import android.os.Bundle
import android.os.Handler
+import android.window.OnBackInvokedDispatcher
+import androidx.annotation.OpenForTesting
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__CANCEL
import com.android.internal.util.FrameworkStatsLog.PRIVACY_TOGGLE_DIALOG_INTERACTION__ACTION__ENABLE
@@ -45,7 +47,8 @@
*
* <p>The dialog is started for the user the app is running for which might be a secondary users.
*/
-class SensorUseStartedActivity @Inject constructor(
+@OpenForTesting
+open class SensorUseStartedActivity @Inject constructor(
private val sensorPrivacyController: IndividualSensorPrivacyController,
private val keyguardStateController: KeyguardStateController,
private val keyguardDismissUtil: KeyguardDismissUtil,
@@ -67,9 +70,10 @@
private lateinit var sensorUsePackageName: String
private var unsuppressImmediately = false
- private lateinit var sensorPrivacyListener: IndividualSensorPrivacyController.Callback
+ private var sensorPrivacyListener: IndividualSensorPrivacyController.Callback? = null
private var mDialog: AlertDialog? = null
+ private val mBackCallback = this::onBackInvoked
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -84,15 +88,14 @@
if (intent.getBooleanExtra(EXTRA_ALL_SENSORS, false)) {
sensor = ALL_SENSORS
- sensorPrivacyListener =
- IndividualSensorPrivacyController.Callback { _, _ ->
- if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
- !sensorPrivacyController.isSensorBlocked(CAMERA)) {
- finish()
- }
- }
-
- sensorPrivacyController.addCallback(sensorPrivacyListener)
+ val callback = IndividualSensorPrivacyController.Callback { _, _ ->
+ if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
+ !sensorPrivacyController.isSensorBlocked(CAMERA)) {
+ finish()
+ }
+ }
+ sensorPrivacyListener = callback
+ sensorPrivacyController.addCallback(callback)
if (!sensorPrivacyController.isSensorBlocked(MICROPHONE) &&
!sensorPrivacyController.isSensorBlocked(CAMERA)) {
finish()
@@ -105,14 +108,14 @@
return
}
}
- sensorPrivacyListener =
- IndividualSensorPrivacyController.Callback { whichSensor: Int,
- isBlocked: Boolean ->
- if (whichSensor == sensor && !isBlocked) {
- finish()
- }
- }
- sensorPrivacyController.addCallback(sensorPrivacyListener)
+ val callback = IndividualSensorPrivacyController.Callback {
+ whichSensor: Int, isBlocked: Boolean ->
+ if (whichSensor == sensor && !isBlocked) {
+ finish()
+ }
+ }
+ sensorPrivacyListener = callback
+ sensorPrivacyController.addCallback(callback)
if (!sensorPrivacyController.isSensorBlocked(sensor)) {
finish()
@@ -122,6 +125,10 @@
mDialog = SensorUseDialog(this, sensor, this, this)
mDialog!!.show()
+
+ onBackInvokedDispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mBackCallback)
}
override fun onStart() {
@@ -180,10 +187,15 @@
override fun onDestroy() {
super.onDestroy()
mDialog?.dismiss()
- sensorPrivacyController.removeCallback(sensorPrivacyListener)
+ sensorPrivacyListener?.also { sensorPrivacyController.removeCallback(it) }
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(mBackCallback)
}
override fun onBackPressed() {
+ onBackInvoked()
+ }
+
+ fun onBackInvoked() {
// do not allow backing out
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
index 63d0d16..31e4464 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/LargeScreenShadeHeaderController.kt
@@ -331,13 +331,8 @@
// Use resources.getXml instead of passing the resource id due to bug b/205018300
header.getConstraintSet(QQS_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.qqs_header))
- val qsConstraints = if (featureFlags.isEnabled(Flags.NEW_HEADER)) {
- R.xml.qs_header_new
- } else {
- R.xml.qs_header
- }
header.getConstraintSet(QS_HEADER_CONSTRAINT)
- .load(context, resources.getXml(qsConstraints))
+ .load(context, resources.getXml(R.xml.qs_header))
header.getConstraintSet(LARGE_SCREEN_HEADER_CONSTRAINT)
.load(context, resources.getXml(R.xml.large_screen_shade_header))
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 6f7b741..e68182e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -279,6 +279,11 @@
private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
private static final Rect M_DUMMY_DIRTY_RECT = new Rect(0, 0, 1, 1);
private static final Rect EMPTY_RECT = new Rect();
+ /**
+ * Duration to use for the animator when the keyguard status view alignment changes, and a
+ * custom clock animation is in use.
+ */
+ private static final int KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION = 1000;
private final StatusBarTouchableRegionManager mStatusBarTouchableRegionManager;
private final Resources mResources;
@@ -675,7 +680,7 @@
};
private final Runnable mMaybeHideExpandedRunnable = () -> {
if (getExpansionFraction() == 0.0f) {
- getView().post(mHideExpandedRunnable);
+ postToView(mHideExpandedRunnable);
}
};
@@ -1592,7 +1597,7 @@
// Use linear here, so the actual clock can pick its own interpolator.
adapter.setInterpolator(Interpolators.LINEAR);
- adapter.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
+ adapter.setDuration(KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION);
adapter.addTarget(clockView);
set.addTransition(adapter);
@@ -2810,7 +2815,7 @@
return top + mNotificationStackScrollLayoutController.getHeight()
+ mSplitShadeNotificationsScrimMarginBottom;
} else {
- return getView().getBottom();
+ return mView.getBottom();
}
}
@@ -2825,7 +2830,7 @@
private int calculateRightQsClippingBound() {
if (mIsFullWidth) {
- return getView().getRight() + mDisplayRightInset;
+ return mView.getRight() + mDisplayRightInset;
} else {
return mNotificationStackScrollLayoutController.getRight();
}
@@ -3509,13 +3514,17 @@
}
private float getHeaderTranslation() {
+ if (mSplitShadeEnabled) {
+ // in split shade QS don't translate, just (un)squish and overshoot
+ return 0;
+ }
if (mBarState == KEYGUARD && !mKeyguardBypassController.getBypassEnabled()) {
return -mQs.getQsMinExpansionHeight();
}
float appearAmount = mNotificationStackScrollLayoutController
.calculateAppearFraction(mExpandedHeight);
float startHeight = -mQsExpansionHeight;
- if (!mSplitShadeEnabled && mBarState == StatusBarState.SHADE) {
+ if (mBarState == StatusBarState.SHADE) {
// Small parallax as we pull down and clip QS
startHeight = -mQsExpansionHeight * QS_PARALLAX_AMOUNT;
}
@@ -5190,6 +5199,26 @@
return mView;
}
+ /** */
+ public boolean postToView(Runnable action) {
+ return mView.post(action);
+ }
+
+ /** */
+ public boolean sendInterceptTouchEventToView(MotionEvent event) {
+ return mView.onInterceptTouchEvent(event);
+ }
+
+ /** */
+ public void requestLayoutOnView() {
+ mView.requestLayout();
+ }
+
+ /** */
+ public void resetViewAlphas() {
+ ViewGroupFadeHelper.reset(mView);
+ }
+
private void beginJankMonitoring() {
if (mInteractionJankMonitor == null) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
index b719177..8698c04 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowControllerImpl.java
@@ -246,7 +246,6 @@
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.setFitInsetsTypes(0 /* types */);
- mLp.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
mLp.setTitle("NotificationShade");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
@@ -384,8 +383,6 @@
mLpChanged.flags |= LayoutParams.FLAG_NOT_FOCUSABLE;
mLpChanged.flags &= ~LayoutParams.FLAG_ALT_FOCUSABLE_IM;
}
-
- mLpChanged.softInputMode = LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
}
private void applyForceShowNavigationFlag(State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 8379e51..d773c01 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -316,7 +316,7 @@
MotionEvent cancellation = MotionEvent.obtain(ev);
cancellation.setAction(MotionEvent.ACTION_CANCEL);
mStackScrollLayout.onInterceptTouchEvent(cancellation);
- mNotificationPanelViewController.getView().onInterceptTouchEvent(cancellation);
+ mNotificationPanelViewController.sendInterceptTouchEventToView(cancellation);
cancellation.recycle();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index eaf7fae..d783293 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -160,7 +160,7 @@
if (getCentralSurfaces().getNotificationShadeWindowView()
.isVisibleToUser()) {
getNotificationPanelViewController().removeOnGlobalLayoutListener(this);
- getNotificationPanelViewController().getView().post(executable);
+ getNotificationPanelViewController().postToView(executable);
}
}
});
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 824d3a3..56b689e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -31,7 +31,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.dagger.CentralSurfacesModule;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.PipelineDumpable;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 585d871..37d82ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -676,9 +676,6 @@
return row != null && row.areChildrenExpanded();
}
- public boolean keepInParent() {
- return row != null && row.keepInParent();
- }
//TODO: probably less confusing to say "is group fully visible"
public boolean isGroupNotFullyVisible() {
@@ -698,10 +695,6 @@
return row != null && row.isSummaryWithChildren();
}
- public void setKeepInParent(boolean keep) {
- if (row != null) row.setKeepInParent(keep);
- }
-
public void onDensityOrFontScaleChanged() {
if (row != null) row.onDensityOrFontScaleChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 3ae2545..65a21a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -1160,12 +1160,21 @@
mLogger.logParentChanged(mIterationCount, prev.getParent(), curr.getParent());
}
- if (curr.getSuppressedChanges().getParent() != null) {
- mLogger.logParentChangeSuppressed(
+ GroupEntry currSuppressedParent = curr.getSuppressedChanges().getParent();
+ GroupEntry prevSuppressedParent = prev.getSuppressedChanges().getParent();
+ if (currSuppressedParent != null && (prevSuppressedParent == null
+ || !prevSuppressedParent.getKey().equals(currSuppressedParent.getKey()))) {
+ mLogger.logParentChangeSuppressedStarted(
mIterationCount,
- curr.getSuppressedChanges().getParent(),
+ currSuppressedParent,
curr.getParent());
}
+ if (prevSuppressedParent != null && currSuppressedParent == null) {
+ mLogger.logParentChangeSuppressedStopped(
+ mIterationCount,
+ prevSuppressedParent,
+ prev.getParent());
+ }
if (curr.getSuppressedChanges().getSection() != null) {
mLogger.logSectionChangeSuppressed(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index 8e052c7..4adc90a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -193,7 +193,7 @@
})
}
- fun logParentChangeSuppressed(
+ fun logParentChangeSuppressedStarted(
buildId: Int,
suppressedParent: GroupEntry?,
keepingParent: GroupEntry?
@@ -207,6 +207,21 @@
})
}
+ fun logParentChangeSuppressedStopped(
+ buildId: Int,
+ previouslySuppressedParent: GroupEntry?,
+ previouslyKeptParent: GroupEntry?
+ ) {
+ buffer.log(TAG, INFO, {
+ long1 = buildId.toLong()
+ str1 = previouslySuppressedParent?.logKey
+ str2 = previouslyKeptParent?.logKey
+ }, {
+ "(Build $long1) Change of parent to '$str1' no longer suppressed; " +
+ "replaced parent '$str2'"
+ })
+ }
+
fun logGroupPruningSuppressed(
buildId: Int,
keepingParent: GroupEntry?
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
index f949af0..8de0381 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/MediaContainerController.kt
@@ -55,4 +55,8 @@
override val view: View
get() = mediaContainerView!!
+
+ override fun offerToKeepInParentForAnimation(): Boolean = false
+ override fun removeFromParentIfKeptForAnimation(): Boolean = false
+ override fun resetKeepInParentForAnimation() {}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
index 26ba12c..ae72a3c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeController.kt
@@ -30,6 +30,7 @@
* below.
*/
interface NodeController {
+
/** A string that uniquely(ish) represents the node in the tree. Used for debugging. */
val nodeLabel: String
@@ -64,6 +65,27 @@
/** Called when this view has been removed */
fun onViewRemoved() {}
+
+ /**
+ * Called before removing a node from its parent
+ *
+ * If returned true, the ShadeViewDiffer won't detach this row and the view system is
+ * responsible for ensuring the row is in eventually removed from the parent.
+ *
+ * @return false to opt out from this feature
+ */
+ fun offerToKeepInParentForAnimation(): Boolean
+
+ /**
+ * Called before a node is reattached. Removes the view from its parent
+ * if it was flagged to be kept before.
+ *
+ * @return whether it did a removal
+ */
+ fun removeFromParentIfKeptForAnimation(): Boolean
+
+ /** Called when a node is being reattached */
+ fun resetKeepInParentForAnimation()
}
/**
@@ -90,7 +112,7 @@
}
private fun treeSpecToStrHelper(tree: NodeSpec, sb: StringBuilder, indent: String) {
- sb.append("${indent}{${tree.controller.nodeLabel}}\n")
+ sb.append("$indent{${tree.controller.nodeLabel}}\n")
if (tree.children.isNotEmpty()) {
val childIndent = "$indent "
for (child in tree.children) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
index 2073e92..5ff686a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
@@ -32,6 +32,9 @@
override val view: View
) : NodeController, PipelineDumpable {
override val nodeLabel: String = "<root>"
+ override fun offerToKeepInParentForAnimation(): Boolean = false
+ override fun removeFromParentIfKeptForAnimation(): Boolean = false
+ override fun resetKeepInParentForAnimation() {}
override fun getChildAt(index: Int): View? {
return listContainer.getContainerChildAt(index)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
index 2c9508e..7b59266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/SectionHeaderController.kt
@@ -100,4 +100,7 @@
override val view: View
get() = _view!!
+ override fun offerToKeepInParentForAnimation(): Boolean = false
+ override fun removeFromParentIfKeptForAnimation(): Boolean = false
+ override fun resetKeepInParentForAnimation() {}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
index 9a9941e..18ee481 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDiffer.kt
@@ -86,10 +86,10 @@
}
private fun maybeDetachChild(
- parentNode: ShadeNode,
- parentSpec: NodeSpec?,
- childNode: ShadeNode,
- childSpec: NodeSpec?
+ parentNode: ShadeNode,
+ parentSpec: NodeSpec?,
+ childNode: ShadeNode,
+ childSpec: NodeSpec?
) {
val newParentNode = childSpec?.parent?.let { getNode(it) }
@@ -100,14 +100,27 @@
nodes.remove(childNode.controller)
}
- logger.logDetachingChild(
- key = childNode.label,
- isTransfer = !childCompletelyRemoved,
- isParentRemoved = parentSpec == null,
- oldParent = parentNode.label,
- newParent = newParentNode?.label)
- parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved)
- childNode.parent = null
+ if (childCompletelyRemoved && parentSpec == null &&
+ childNode.offerToKeepInParentForAnimation()) {
+ // If both the child and the parent are being removed at the same time, then
+ // keep the child attached to the parent for animation purposes
+ logger.logSkipDetachingChild(
+ key = childNode.label,
+ parentKey = parentNode.label,
+ isTransfer = !childCompletelyRemoved,
+ isParentRemoved = true
+ )
+ } else {
+ logger.logDetachingChild(
+ key = childNode.label,
+ isTransfer = !childCompletelyRemoved,
+ isParentRemoved = parentSpec == null,
+ oldParent = parentNode.label,
+ newParent = newParentNode?.label
+ )
+ parentNode.removeChild(childNode, isTransfer = !childCompletelyRemoved)
+ childNode.parent = null
+ }
}
}
@@ -119,6 +132,16 @@
val childNode = getNode(childSpec)
if (childNode.view != currView) {
+ val removedFromParent = childNode.removeFromParentIfKeptForAnimation()
+ if (removedFromParent) {
+ logger.logDetachingChild(
+ key = childNode.label,
+ isTransfer = false,
+ isParentRemoved = true,
+ oldParent = null,
+ newParent = null
+ )
+ }
when (childNode.parent) {
null -> {
@@ -142,6 +165,8 @@
}
}
+ childNode.resetKeepInParentForAnimation()
+
if (childSpec.children.isNotEmpty()) {
attachChildren(childNode, specMap)
}
@@ -213,4 +238,16 @@
controller.removeChild(child.controller, isTransfer)
child.controller.onViewRemoved()
}
+
+ fun offerToKeepInParentForAnimation(): Boolean {
+ return controller.offerToKeepInParentForAnimation()
+ }
+
+ fun removeFromParentIfKeptForAnimation(): Boolean {
+ return controller.removeFromParentIfKeptForAnimation()
+ }
+
+ fun resetKeepInParentForAnimation() {
+ controller.resetKeepInParentForAnimation()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
index b4b9438..1e22c2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
@@ -43,6 +43,20 @@
})
}
+ fun logSkipDetachingChild(
+ key: String,
+ parentKey: String?,
+ isTransfer: Boolean,
+ isParentRemoved: Boolean
+ ) {
+ buffer.log(TAG, LogLevel.DEBUG, {
+ str1 = key
+ str2 = parentKey
+ bool1 = isTransfer
+ bool2 = isParentRemoved
+ }, { "Skip detaching $str1 from $str2 isTransfer=$bool1 isParentRemoved=$bool2" })
+ }
+
fun logAttachingChild(key: String, parent: String, atIndex: Int) {
buffer.log(TAG, LogLevel.DEBUG, {
str1 = key
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index b93e150..1eccc98 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -240,7 +240,7 @@
private NotificationContentView mPrivateLayout;
private NotificationContentView[] mLayouts;
private int mNotificationColor;
- private ExpansionLogger mLogger;
+ private ExpandableNotificationRowLogger mLogger;
private String mLoggingKey;
private NotificationGuts mGuts;
private NotificationEntry mEntry;
@@ -339,7 +339,7 @@
}
}
};
- private boolean mKeepInParent;
+ private boolean mKeepInParentForDismissAnimation;
private boolean mRemoved;
private static final Property<ExpandableNotificationRow, Float> TRANSLATE_CONTENT =
new FloatProperty<ExpandableNotificationRow>("translate") {
@@ -825,6 +825,12 @@
if (mChildrenContainer == null) {
mChildrenContainerStub.inflate();
}
+
+ if (row.keepInParentForDismissAnimation()) {
+ logSkipAttachingKeepInParentChild(row);
+ return;
+ }
+
mChildrenContainer.addNotification(row, childIndex);
onAttachedChildrenCountChanged();
row.setIsChildInGroup(true, this);
@@ -833,6 +839,7 @@
public void removeChildNotification(ExpandableNotificationRow row) {
if (mChildrenContainer != null) {
mChildrenContainer.removeNotification(row);
+ row.setKeepInParentForDismissAnimation(false);
}
onAttachedChildrenCountChanged();
row.setIsChildInGroup(false, null);
@@ -840,6 +847,31 @@
}
/**
+ * Removes the children notifications which were marked to keep for the dismissal animation.
+ */
+ public void removeChildrenWithKeepInParent() {
+ if (mChildrenContainer == null) return;
+
+ List<ExpandableNotificationRow> clonedList = new ArrayList<>(
+ mChildrenContainer.getAttachedChildren());
+ boolean childCountChanged = false;
+ for (ExpandableNotificationRow child : clonedList) {
+ if (child.keepInParentForDismissAnimation()) {
+ mChildrenContainer.removeNotification(child);
+ child.setIsChildInGroup(false, null);
+ child.requestBottomRoundness(0.0f, /* animate = */ false, SourceType.DefaultValue);
+ child.setKeepInParentForDismissAnimation(false);
+ logKeepInParentChildDetached(child);
+ childCountChanged = true;
+ }
+ }
+
+ if (childCountChanged) {
+ onAttachedChildrenCountChanged();
+ }
+ }
+
+ /**
* Returns the child notification at [index], or null if no such child.
*/
@Nullable
@@ -1361,12 +1393,15 @@
}
}
- public boolean keepInParent() {
- return mKeepInParent;
+ /**
+ * @return if this entry should be kept in its parent during removal.
+ */
+ public boolean keepInParentForDismissAnimation() {
+ return mKeepInParentForDismissAnimation;
}
- public void setKeepInParent(boolean keepInParent) {
- mKeepInParent = keepInParent;
+ public void setKeepInParentForDismissAnimation(boolean keepInParent) {
+ mKeepInParentForDismissAnimation = keepInParent;
}
@Override
@@ -1537,8 +1572,29 @@
mUseIncreasedHeadsUpHeight = use;
}
- public interface ExpansionLogger {
+ /**
+ * Interface for logging {{@link ExpandableNotificationRow} events.}
+ */
+ public interface ExpandableNotificationRowLogger {
+ /**
+ * Called when the notification is expanded / collapsed.
+ */
void logNotificationExpansion(String key, boolean userAction, boolean expanded);
+
+ /**
+ * Called when a notification which was previously kept in its parent for the
+ * dismiss animation is finally detached from its parent.
+ */
+ void logKeepInParentChildDetached(NotificationEntry child, NotificationEntry oldParent);
+
+ /**
+ * Called when we want to attach a notification to a new parent,
+ * but it still has the keepInParent flag set, so we skip it.
+ */
+ void logSkipAttachingKeepInParentChild(
+ NotificationEntry child,
+ NotificationEntry newParent
+ );
}
public ExpandableNotificationRow(Context context, AttributeSet attrs) {
@@ -1556,7 +1612,7 @@
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
String appName,
String notificationKey,
- ExpansionLogger logger,
+ ExpandableNotificationRowLogger logger,
KeyguardBypassController bypassController,
GroupMembershipManager groupMembershipManager,
GroupExpansionManager groupExpansionManager,
@@ -3567,6 +3623,18 @@
});
}
+ private void logKeepInParentChildDetached(ExpandableNotificationRow child) {
+ if (mLogger != null) {
+ mLogger.logKeepInParentChildDetached(child.getEntry(), getEntry());
+ }
+ }
+
+ private void logSkipAttachingKeepInParentChild(ExpandableNotificationRow child) {
+ if (mLogger != null) {
+ mLogger.logSkipAttachingKeepInParentChild(child.getEntry(), getEntry());
+ }
+ }
+
private void setTargetPoint(Point p) {
mTargetPoint = p;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 842526e..f9e9a2d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -33,9 +33,9 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.FeedbackIcon;
@@ -83,13 +83,11 @@
private final GroupExpansionManager mGroupExpansionManager;
private final RowContentBindStage mRowContentBindStage;
private final NotificationLogger mNotificationLogger;
+ private final NotificationRowLogger mLogBufferLogger;
private final HeadsUpManager mHeadsUpManager;
private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
private final StatusBarStateController mStatusBarStateController;
private final MetricsLogger mMetricsLogger;
-
- private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
- this::logNotificationExpansion;
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
private final NotificationGutsManager mNotificationGutsManager;
private final OnUserInteractionCallback mOnUserInteractionCallback;
@@ -101,8 +99,32 @@
private final Optional<BubblesManager> mBubblesManagerOptional;
private final SmartReplyConstants mSmartReplyConstants;
private final SmartReplyController mSmartReplyController;
-
private final ExpandableNotificationRowDragController mDragController;
+ private final ExpandableNotificationRow.ExpandableNotificationRowLogger mLoggerCallback =
+ new ExpandableNotificationRow.ExpandableNotificationRowLogger() {
+ @Override
+ public void logNotificationExpansion(String key, boolean userAction,
+ boolean expanded) {
+ mNotificationLogger.onExpansionChanged(key, userAction, expanded);
+ }
+
+ @Override
+ public void logKeepInParentChildDetached(
+ NotificationEntry child,
+ NotificationEntry oldParent
+ ) {
+ mLogBufferLogger.logKeepInParentChildDetached(child, oldParent);
+ }
+
+ @Override
+ public void logSkipAttachingKeepInParentChild(
+ NotificationEntry child,
+ NotificationEntry newParent
+ ) {
+ mLogBufferLogger.logSkipAttachingKeepInParentChild(child, newParent);
+ }
+ };
+
@Inject
public ExpandableNotificationRowController(
@@ -110,6 +132,7 @@
ActivatableNotificationViewController activatableNotificationViewController,
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
MetricsLogger metricsLogger,
+ NotificationRowLogger logBufferLogger,
NotificationListContainer listContainer,
NotificationMediaManager mediaManager,
SmartReplyConstants smartReplyConstants,
@@ -163,6 +186,7 @@
mBubblesManagerOptional = bubblesManagerOptional;
mDragController = dragController;
mMetricsLogger = metricsLogger;
+ mLogBufferLogger = logBufferLogger;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
}
@@ -177,7 +201,7 @@
mRemoteInputViewSubcomponentFactory,
mAppName,
mNotificationKey,
- mExpansionLogger,
+ mLoggerCallback,
mKeyguardBypassController,
mGroupMembershipManager,
mGroupExpansionManager,
@@ -243,10 +267,6 @@
}
};
- private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
- mNotificationLogger.onExpansionChanged(key, userAction, expanded);
- }
-
@Override
@NonNull
public String getNodeLabel() {
@@ -336,4 +356,29 @@
public void setFeedbackIcon(@Nullable FeedbackIcon icon) {
mView.setFeedbackIcon(icon);
}
+
+ @Override
+ public boolean offerToKeepInParentForAnimation() {
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)) {
+ mView.setKeepInParentForDismissAnimation(true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean removeFromParentIfKeptForAnimation() {
+ ExpandableNotificationRow parent = mView.getNotificationParent();
+ if (mView.keepInParentForDismissAnimation() && parent != null) {
+ parent.removeChildNotification(mView);
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public void resetKeepInParentForAnimation() {
+ mView.setKeepInParentForDismissAnimation(false);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
new file mode 100644
index 0000000..ce11be3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2022 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.notification.row
+
+import com.android.systemui.log.dagger.NotificationLog
+import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.logKey
+import javax.inject.Inject
+
+class NotificationRowLogger @Inject constructor(@NotificationLog private val buffer: LogBuffer) {
+ fun logKeepInParentChildDetached(child: NotificationEntry, oldParent: NotificationEntry?) {
+ buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = child.logKey
+ str2 = oldParent.logKey
+ },
+ { "Detach child $str1 kept in parent $str2" }
+ )
+ }
+
+ fun logSkipAttachingKeepInParentChild(child: NotificationEntry, newParent: NotificationEntry?) {
+ buffer.log(
+ TAG,
+ LogLevel.WARNING,
+ {
+ str1 = child.logKey
+ str2 = newParent.logKey
+ },
+ { "Skipping to attach $str1 to $str2, because it still flagged to keep in parent" }
+ )
+ }
+}
+
+private const val TAG = "NotifRow"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 962eeb9..2c096f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -32,6 +32,7 @@
import android.animation.TimeAnimator;
import android.animation.ValueAnimator;
import android.annotation.ColorInt;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -191,6 +192,7 @@
/** Used to track the Y positions that were already used to draw debug text labels. */
private Set<Integer> mDebugTextUsedYPositions;
private final boolean mDebugRemoveAnimation;
+ private final boolean mSimplifiedAppearFraction;
private int mContentHeight;
private float mIntrinsicContentHeight;
@@ -571,6 +573,7 @@
FeatureFlags featureFlags = Dependency.get(FeatureFlags.class);
mDebugLines = featureFlags.isEnabled(Flags.NSSL_DEBUG_LINES);
mDebugRemoveAnimation = featureFlags.isEnabled(Flags.NSSL_DEBUG_REMOVE_ANIMATION);
+ mSimplifiedAppearFraction = featureFlags.isEnabled(Flags.SIMPLIFIED_APPEAR_FRACTION);
mSectionsManager = Dependency.get(NotificationSectionsManager.class);
mScreenOffAnimationController =
Dependency.get(ScreenOffAnimationController.class);
@@ -1411,10 +1414,8 @@
}
int stackHeight;
float translationY;
- float appearEndPosition = getAppearEndPosition();
- float appearStartPosition = getAppearStartPosition();
float appearFraction = 1.0f;
- boolean appearing = height < appearEndPosition;
+ boolean appearing = calculateAppearFraction(height) < 1;
mAmbientState.setAppearing(appearing);
if (!appearing) {
translationY = 0;
@@ -1445,11 +1446,14 @@
} else {
// This may happen when pushing up a heads up. We linearly push it up from the
// start
- translationY = height - appearStartPosition + getExpandTranslationStart();
+ translationY = height - getAppearStartPosition() + getExpandTranslationStart();
}
stackHeight = (int) (height - translationY);
- if (isHeadsUpTransition()) {
- translationY = MathUtils.lerp(mHeadsUpInset - mTopPadding, 0, appearFraction);
+ if (isHeadsUpTransition() && appearFraction >= 0) {
+ int topSpacing = mShouldUseSplitNotificationShade
+ ? mAmbientState.getStackTopMargin() : mTopPadding;
+ float startPos = mHeadsUpInset - topSpacing;
+ translationY = MathUtils.lerp(startPos, 0, appearFraction);
}
}
mAmbientState.setAppearFraction(appearFraction);
@@ -1581,7 +1585,7 @@
*/
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
private float getAppearEndPosition() {
- int appearPosition = 0;
+ int appearPosition = mAmbientState.getStackTopMargin();
int visibleNotifCount = mController.getVisibleNotificationCount();
if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
if (isHeadsUpTransition()
@@ -1605,18 +1609,49 @@
return mAmbientState.getTrackedHeadsUpRow() != null;
}
- /**
- * @param height the height of the panel
- * @return the fraction of the appear animation that has been performed
- */
+ // TODO(b/246353296): remove it when Flags.SIMPLIFIED_APPEAR_FRACTION is removed
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
- public float calculateAppearFraction(float height) {
+ public float calculateAppearFractionOld(float height) {
float appearEndPosition = getAppearEndPosition();
float appearStartPosition = getAppearStartPosition();
return (height - appearStartPosition)
/ (appearEndPosition - appearStartPosition);
}
+ /**
+ * @param height the height of the panel
+ * @return Fraction of the appear animation that has been performed. Normally follows expansion
+ * fraction so goes from 0 to 1, the only exception is HUN where it can go negative, down to -1,
+ * when HUN is swiped up.
+ */
+ @FloatRange(from = -1.0, to = 1.0)
+ public float simplifiedAppearFraction(float height) {
+ if (isHeadsUpTransition()) {
+ // HUN is a special case because fraction can go negative if swiping up. And for now
+ // it must go negative as other pieces responsible for proper translation up assume
+ // negative value for HUN going up.
+ // This can't use expansion fraction as that goes only from 0 to 1. Also when
+ // appear fraction for HUN is 0, expansion fraction will be already around 0.2-0.3
+ // and that makes translation jump immediately. Let's use old implementation for now and
+ // see if we can figure out something better
+ return MathUtils.constrain(calculateAppearFractionOld(height), -1, 1);
+ } else {
+ return mAmbientState.getExpansionFraction();
+ }
+ }
+
+ public float calculateAppearFraction(float height) {
+ if (mSimplifiedAppearFraction) {
+ return simplifiedAppearFraction(height);
+ } else if (mShouldUseSplitNotificationShade) {
+ // for split shade we want to always use the new way of calculating appear fraction
+ // because without it heads-up experience is very broken and it's less risky change
+ return simplifiedAppearFraction(height);
+ } else {
+ return calculateAppearFractionOld(height);
+ }
+ }
+
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public float getStackTranslation() {
return mStackTranslation;
@@ -2739,6 +2774,10 @@
}
} else {
mSwipedOutViews.remove(child);
+
+ if (child instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) child).removeChildrenWithKeepInParent();
+ }
}
updateAnimationState(false, child);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 0240bbc..ad4501a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -443,7 +443,11 @@
if (!row.isDismissed()) {
handleChildViewDismissed(view);
}
+
row.removeFromTransientContainer();
+ if (row instanceof ExpandableNotificationRow) {
+ ((ExpandableNotificationRow) row).removeChildrenWithKeepInParent();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index f72f1bc..182d397 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -763,6 +763,15 @@
}
@Override
+ public void onKeyguardBouncerStateChanged(boolean bouncerIsOrWillBeShowing) {
+ // When the bouncer is dismissed, treat this as a reset of the unlock mode. The user
+ // may have gone back instead of successfully unlocking
+ if (!bouncerIsOrWillBeShowing) {
+ resetMode();
+ }
+ }
+
+ @Override
public void dump(PrintWriter pw, String[] args) {
pw.println(" BiometricUnlockController:");
pw.print(" mMode="); pw.println(mMode);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index a592da4..bc2ee1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -168,6 +168,7 @@
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -183,7 +184,6 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.AutoHideUiElement;
import com.android.systemui.statusbar.BackDropView;
import com.android.systemui.statusbar.CircleReveal;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
index 3811689..4fe03017 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java
@@ -25,7 +25,7 @@
import com.android.systemui.plugins.NotificationListenerController;
import com.android.systemui.plugins.NotificationListenerController.NotificationProvider;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index c527f30..fb0d3e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -110,6 +110,12 @@
private boolean mClipsQsScrim;
/**
+ * Whether an activity is launching over the lockscreen. During the launch animation, we want to
+ * delay certain scrim changes until after the animation ends.
+ */
+ private boolean mOccludeAnimationPlaying = false;
+
+ /**
* The amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
* shade.
@@ -733,6 +739,11 @@
return mClipsQsScrim;
}
+ public void setOccludeAnimationPlaying(boolean occludeAnimationPlaying) {
+ mOccludeAnimationPlaying = occludeAnimationPlaying;
+ applyAndDispatchState();
+ }
+
private void setOrAdaptCurrentAnimation(@Nullable View scrim) {
if (scrim == null) {
return;
@@ -772,11 +783,15 @@
}
if (mState == ScrimState.UNLOCKED || mState == ScrimState.DREAMING) {
- // Darken scrim as you pull down the shade when unlocked, unless the shade is expanding
- // because we're doing the screen off animation OR the shade is collapsing because
- // we're playing the unlock animation
+ final boolean occluding =
+ mOccludeAnimationPlaying || mState.mLaunchingAffordanceWithPreview;
+
+ // Darken scrim as it's pulled down while unlocked. If we're unlocked but playing the
+ // screen off/occlusion animations, ignore expansion changes while those animations
+ // play.
if (!mScreenOffAnimationController.shouldExpandNotifications()
- && !mAnimatingPanelExpansionOnUnlock) {
+ && !mAnimatingPanelExpansionOnUnlock
+ && !occluding) {
float behindFraction = getInterpolatedFraction();
behindFraction = (float) Math.pow(behindFraction, 0.8f);
if (mClipsQsScrim) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
index 5512bed..3d6bebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeadsUpChangeListener.java
@@ -72,9 +72,9 @@
//resize the layout. Let's
// make sure that the window stays small for one frame until the
//touchableRegion is set.
- mNotificationPanelViewController.getView().requestLayout();
+ mNotificationPanelViewController.requestLayoutOnView();
mNotificationShadeWindowController.setForceWindowCollapsed(true);
- mNotificationPanelViewController.getView().post(() -> {
+ mNotificationPanelViewController.postToView(() -> {
mNotificationShadeWindowController.setForceWindowCollapsed(false);
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 01a1ebe..fcf33b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -20,6 +20,7 @@
import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER;
+import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_SHOW_BOUNCER;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_UNLOCK_COLLAPSING;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING;
@@ -77,7 +78,6 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.ViewGroupFadeHelper;
import com.android.systemui.statusbar.phone.KeyguardBouncer.PrimaryBouncerExpansionCallback;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -218,7 +218,7 @@
protected LockPatternUtils mLockPatternUtils;
protected ViewMediatorCallback mViewMediatorCallback;
- protected CentralSurfaces mCentralSurfaces;
+ @Nullable protected CentralSurfaces mCentralSurfaces;
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
private boolean mCentralSurfacesRegistered;
@@ -266,7 +266,7 @@
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private final LatencyTracker mLatencyTracker;
private final KeyguardSecurityModel mKeyguardSecurityModel;
- private KeyguardBypassController mBypassController;
+ @Nullable private KeyguardBypassController mBypassController;
@Nullable private AlternateBouncer mAlternateBouncer;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
@@ -478,6 +478,7 @@
} else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
+ && !(mBiometricUnlockController.getMode() == MODE_SHOW_BOUNCER)
&& !isUnlockCollapsing()) {
if (mPrimaryBouncer != null) {
mPrimaryBouncer.setExpansion(fraction);
@@ -742,6 +743,12 @@
}
private void updateAlternateBouncerShowing(boolean updateScrim) {
+ if (!mCentralSurfacesRegistered) {
+ // if CentralSurfaces hasn't been registered yet, then the controllers below haven't
+ // been initialized yet so there's no need to attempt to forward them events.
+ return;
+ }
+
final boolean isShowingAlternateBouncer = isShowingAlternateBouncer();
if (mKeyguardMessageAreaController != null) {
mKeyguardMessageAreaController.setIsVisible(isShowingAlternateBouncer);
@@ -1009,7 +1016,7 @@
public void onKeyguardFadedAway() {
mNotificationContainer.postDelayed(() -> mNotificationShadeWindowController
.setKeyguardFadingAway(false), 100);
- ViewGroupFadeHelper.reset(mNotificationPanelViewController.getView());
+ mNotificationPanelViewController.resetViewAlphas();
mCentralSurfaces.finishKeyguardFadingAway();
mBiometricUnlockController.finishKeyguardFadingAway();
WindowManagerGlobal.getInstance().trimMemory(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
index 4c6c7e0..3d0e69c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java
@@ -22,7 +22,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
index 26cc6ba3..f3b9cc1 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -25,8 +25,8 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.ToastPlugin;
-import com.android.systemui.shared.plugins.PluginManager;
import java.io.PrintWriter;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
index fe183fc..4999515 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java
@@ -38,9 +38,9 @@
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.PluginEnablerImpl;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginActionManager;
import com.android.systemui.shared.plugins.PluginEnabler;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.shared.plugins.PluginPrefs;
import java.util.List;
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index 0f06144..6216acd 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -92,6 +92,7 @@
deviceStateManager.registerCallback(executor, FoldListener())
wakefulnessLifecycle.addObserver(this)
+ // TODO(b/254878364): remove this call to NPVC.getView()
centralSurfaces.notificationPanelViewController.view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) { listenForDozing(this) }
}
@@ -157,6 +158,7 @@
// We don't need to wait for the scrim as it is already displayed
// but we should wait for the initial animation preparations to be drawn
// (setting initial alpha/translation)
+ // TODO(b/254878364): remove this call to NPVC.getView()
OneShotPreDrawListener.add(
centralSurfaces.notificationPanelViewController.view,
onReady
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
index 6ed3a09..ae30ca0 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldLightRevealOverlayAnimation.kt
@@ -15,7 +15,7 @@
*/
package com.android.systemui.unfold
-import android.animation.ValueAnimator
+import android.content.ContentResolver
import android.content.Context
import android.graphics.PixelFormat
import android.hardware.devicestate.DeviceStateManager
@@ -39,6 +39,7 @@
import com.android.systemui.statusbar.LinearLightRevealEffect
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.updates.RotationChangeProvider
+import com.android.systemui.unfold.util.ScaleAwareTransitionProgressProvider.Companion.areAnimationsEnabled
import com.android.systemui.util.traceSection
import com.android.wm.shell.displayareahelper.DisplayAreaHelper
import java.util.Optional
@@ -52,6 +53,7 @@
constructor(
private val context: Context,
private val deviceStateManager: DeviceStateManager,
+ private val contentResolver: ContentResolver,
private val displayManager: DisplayManager,
private val unfoldTransitionProgressProvider: UnfoldTransitionProgressProvider,
private val displayAreaHelper: Optional<DisplayAreaHelper>,
@@ -92,7 +94,7 @@
overlayContainer = builder.build()
SurfaceControl.Transaction()
- .setLayer(overlayContainer, Integer.MAX_VALUE)
+ .setLayer(overlayContainer, UNFOLD_OVERLAY_LAYER_Z_INDEX)
.show(overlayContainer)
.apply()
@@ -117,7 +119,7 @@
Trace.beginSection("UnfoldLightRevealOverlayAnimation#onScreenTurningOn")
try {
// Add the view only if we are unfolding and this is the first screen on
- if (!isFolded && !isUnfoldHandled && ValueAnimator.areAnimatorsEnabled()) {
+ if (!isFolded && !isUnfoldHandled && contentResolver.areAnimationsEnabled()) {
addView(onOverlayReady)
isUnfoldHandled = true
} else {
@@ -162,11 +164,10 @@
// blocker (turn on the brightness) only when the content is actually visible as it
// might be presented only in the next frame.
// See b/197538198
- transaction
- .setFrameTimelineVsync(vsyncId)
- .apply()
+ transaction.setFrameTimelineVsync(vsyncId).apply()
- transaction.setFrameTimelineVsync(vsyncId + 1)
+ transaction
+ .setFrameTimelineVsync(vsyncId + 1)
.addTransactionCommittedListener(backgroundExecutor) {
Trace.endAsyncSection("UnfoldLightRevealOverlayAnimation#relayout", 0)
callback.run()
@@ -218,8 +219,7 @@
}
private fun getUnfoldedDisplayInfo(): DisplayInfo =
- displayManager
- .displays
+ displayManager.displays
.asSequence()
.map { DisplayInfo().apply { it.getDisplayInfo(this) } }
.filter { it.type == Display.TYPE_INTERNAL }
@@ -266,5 +266,14 @@
isUnfoldHandled = false
}
this.isFolded = isFolded
- })
+ }
+ )
+
+ private companion object {
+ private const val ROTATION_ANIMATION_OVERLAY_Z_INDEX = Integer.MAX_VALUE
+
+ // Put the unfold overlay below the rotation animation screenshot to hide the moment
+ // when it is rotated but the rotation of the other windows hasn't happen yet
+ private const val UNFOLD_OVERLAY_LAYER_Z_INDEX = ROTATION_ANIMATION_OVERLAY_Z_INDEX - 1
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
index 83f0711..6672469 100644
--- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserInteractor.kt
@@ -66,7 +66,6 @@
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -117,7 +116,7 @@
private val callbacks = mutableSetOf<UserCallback>()
private val userInfos =
combine(repository.userSwitcherSettings, repository.userInfos) { settings, userInfos ->
- userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }
+ userInfos.filter { !it.isGuest || canCreateGuestUser(settings) }.filter { it.isFull }
}
/** List of current on-device users to select from. */
@@ -138,18 +137,12 @@
/** The currently-selected user. */
val selectedUser: Flow<UserModel>
get() =
- combine(
- repository.selectedUserInfo,
- repository.userSwitcherSettings,
- ) { selectedUserInfo, settings ->
+ repository.selectedUserInfo.map { selectedUserInfo ->
val selectedUserId = selectedUserInfo.id
- checkNotNull(
- toUserModel(
- userInfo = selectedUserInfo,
- selectedUserId = selectedUserId,
- canSwitchUsers = canSwitchUsers(selectedUserId),
- isUserSwitcherEnabled = settings.isUserSwitcherEnabled,
- )
+ toUserModel(
+ userInfo = selectedUserInfo,
+ selectedUserId = selectedUserId,
+ canSwitchUsers = canSwitchUsers(selectedUserId)
)
}
@@ -629,7 +622,7 @@
// The guest user should go in the last position.
.sortedBy { it.isGuest }
.mapNotNull { userInfo ->
- toUserModel(
+ filterAndMapToUserModel(
userInfo = userInfo,
selectedUserId = selectedUserId,
canSwitchUsers = canSwitchUsers,
@@ -638,51 +631,65 @@
}
}
- private suspend fun toUserModel(
+ /**
+ * Maps UserInfo to UserModel based on some parameters and return null under certain conditions
+ * to be filtered out.
+ */
+ private suspend fun filterAndMapToUserModel(
userInfo: UserInfo,
selectedUserId: Int,
canSwitchUsers: Boolean,
isUserSwitcherEnabled: Boolean,
): UserModel? {
- val userId = userInfo.id
- val isSelected = userId == selectedUserId
-
return when {
// When the user switcher is not enabled in settings, we only show the primary user.
!isUserSwitcherEnabled && !userInfo.isPrimary -> null
-
// We avoid showing disabled users.
!userInfo.isEnabled -> null
- userInfo.isGuest ->
- UserModel(
- id = userId,
- name = Text.Loaded(userInfo.name),
- image =
- getUserImage(
- isGuest = true,
- userId = userId,
- ),
- isSelected = isSelected,
- isSelectable = canSwitchUsers,
- isGuest = true,
- )
- userInfo.supportsSwitchToByUser() ->
- UserModel(
- id = userId,
- name = Text.Loaded(userInfo.name),
- image =
- getUserImage(
- isGuest = false,
- userId = userId,
- ),
- isSelected = isSelected,
- isSelectable = canSwitchUsers || isSelected,
- isGuest = false,
- )
+ // We meet the conditions to return the UserModel.
+ userInfo.isGuest || userInfo.supportsSwitchToByUser() ->
+ toUserModel(userInfo, selectedUserId, canSwitchUsers)
else -> null
}
}
+ /** Maps UserInfo to UserModel based on some parameters. */
+ private suspend fun toUserModel(
+ userInfo: UserInfo,
+ selectedUserId: Int,
+ canSwitchUsers: Boolean
+ ): UserModel {
+ val userId = userInfo.id
+ val isSelected = userId == selectedUserId
+ return if (userInfo.isGuest) {
+ UserModel(
+ id = userId,
+ name = Text.Loaded(userInfo.name),
+ image =
+ getUserImage(
+ isGuest = true,
+ userId = userId,
+ ),
+ isSelected = isSelected,
+ isSelectable = canSwitchUsers,
+ isGuest = true,
+ )
+ } else {
+ UserModel(
+ id = userId,
+ name = Text.Loaded(userInfo.name),
+ image =
+ getUserImage(
+ isGuest = false,
+ userId = userId,
+ ),
+ isSelected = isSelected,
+ isSelectable = canSwitchUsers || isSelected,
+ isGuest = false,
+ )
+ }
+ }
+
private suspend fun canSwitchUsers(selectedUserId: Int): Boolean {
return withContext(backgroundDispatcher) {
manager.getUserSwitchability(UserHandle.of(selectedUserId))
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
index 4875982..9b06a37 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/AsyncSensorManager.java
@@ -31,8 +31,8 @@
import com.android.internal.util.Preconditions;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.plugins.PluginListener;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.concurrency.ThreadFactory;
import java.util.ArrayList;
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 0d96272..63b98bb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -72,6 +72,7 @@
import android.os.SystemClock;
import android.os.Trace;
import android.os.VibrationEffect;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
@@ -108,6 +109,8 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.view.RotationPolicy;
@@ -125,11 +128,15 @@
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
import com.android.systemui.util.AlphaTintDrawableWrapper;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.util.RoundedCornerProgressDrawable;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
/**
@@ -186,6 +193,9 @@
private ViewGroup mDialogRowsView;
private ViewGroup mRinger;
+ private DeviceConfigProxy mDeviceConfigProxy;
+ private Executor mExecutor;
+
/**
* Container for the top part of the dialog, which contains the ringer, the ringer drawer, the
* volume rows, and the ellipsis button. This does not include the live caption button.
@@ -274,6 +284,13 @@
private BackgroundBlurDrawable mDialogRowsViewBackground;
private final InteractionJankMonitor mInteractionJankMonitor;
+ private boolean mSeparateNotification;
+
+ @VisibleForTesting
+ int mVolumeRingerIconDrawableId;
+ @VisibleForTesting
+ int mVolumeRingerMuteIconDrawableId;
+
public VolumeDialogImpl(
Context context,
VolumeDialogController volumeDialogController,
@@ -283,7 +300,9 @@
MediaOutputDialogFactory mediaOutputDialogFactory,
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ DeviceConfigProxy deviceConfigProxy,
+ Executor executor) {
mContext =
new ContextThemeWrapper(context, R.style.volume_dialog_theme);
mController = volumeDialogController;
@@ -323,6 +342,50 @@
}
initDimens();
+
+ mDeviceConfigProxy = deviceConfigProxy;
+ mExecutor = executor;
+ mSeparateNotification = mDeviceConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+ updateRingerModeIconSet();
+ }
+
+ /**
+ * If ringer and notification are the same stream (T and earlier), use notification-like bell
+ * icon set.
+ * If ringer and notification are separated, then use generic speaker icons.
+ */
+ private void updateRingerModeIconSet() {
+ if (mSeparateNotification) {
+ mVolumeRingerIconDrawableId = R.drawable.ic_speaker_on;
+ mVolumeRingerMuteIconDrawableId = R.drawable.ic_speaker_mute;
+ } else {
+ mVolumeRingerIconDrawableId = R.drawable.ic_volume_ringer;
+ mVolumeRingerMuteIconDrawableId = R.drawable.ic_volume_ringer_mute;
+ }
+
+ if (mRingerDrawerMuteIcon != null) {
+ mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ }
+ if (mRingerDrawerNormalIcon != null) {
+ mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
+ }
+ }
+
+ /**
+ * Change icon for ring stream (not ringer mode icon)
+ */
+ private void updateRingRowIcon() {
+ Optional<VolumeRow> volumeRow = mRows.stream().filter(row -> row.stream == STREAM_RING)
+ .findFirst();
+ if (volumeRow.isPresent()) {
+ VolumeRow volRow = volumeRow.get();
+ volRow.iconRes = mSeparateNotification ? R.drawable.ic_ring_volume
+ : R.drawable.ic_volume_ringer;
+ volRow.iconMuteRes = mSeparateNotification ? R.drawable.ic_ring_volume_off
+ : R.drawable.ic_volume_ringer_mute;
+ volRow.setIcon(volRow.iconRes, mContext.getTheme());
+ }
}
@Override
@@ -339,6 +402,9 @@
mController.getState();
mConfigurationController.addCallback(this);
+
+ mDeviceConfigProxy.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI,
+ mExecutor, this::onDeviceConfigChange);
}
@Override
@@ -346,6 +412,24 @@
mController.removeCallback(mControllerCallbackH);
mHandler.removeCallbacksAndMessages(null);
mConfigurationController.removeCallback(this);
+ mDeviceConfigProxy.removeOnPropertiesChangedListener(this::onDeviceConfigChange);
+ }
+
+ /**
+ * Update ringer mode icon based on the config
+ */
+ private void onDeviceConfigChange(DeviceConfig.Properties properties) {
+ Set<String> changeSet = properties.getKeyset();
+ if (changeSet.contains(SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION)) {
+ boolean newVal = properties.getBoolean(
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, false);
+ if (newVal != mSeparateNotification) {
+ mSeparateNotification = newVal;
+ updateRingerModeIconSet();
+ updateRingRowIcon();
+
+ }
+ }
}
@Override
@@ -552,6 +636,9 @@
mRingerDrawerNormalIcon = mDialog.findViewById(R.id.volume_drawer_normal_icon);
mRingerDrawerNewSelectionBg = mDialog.findViewById(R.id.volume_drawer_selection_background);
+ mRingerDrawerMuteIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ mRingerDrawerNormalIcon.setImageResource(mVolumeRingerIconDrawableId);
+
setupRingerDrawer();
mODICaptionsView = mDialog.findViewById(R.id.odi_captions);
@@ -575,8 +662,14 @@
addRow(AudioManager.STREAM_MUSIC,
R.drawable.ic_volume_media, R.drawable.ic_volume_media_mute, true, true);
if (!AudioSystem.isSingleVolume(mContext)) {
- addRow(AudioManager.STREAM_RING,
- R.drawable.ic_volume_ringer, R.drawable.ic_volume_ringer_mute, true, false);
+ if (mSeparateNotification) {
+ addRow(AudioManager.STREAM_RING, R.drawable.ic_ring_volume,
+ R.drawable.ic_ring_volume_off, true, false);
+ } else {
+ addRow(AudioManager.STREAM_RING, R.drawable.ic_volume_ringer,
+ R.drawable.ic_volume_ringer, true, false);
+ }
+
addRow(STREAM_ALARM,
R.drawable.ic_alarm, R.drawable.ic_volume_alarm_mute, true, false);
addRow(AudioManager.STREAM_VOICE_CALL,
@@ -1532,8 +1625,8 @@
mRingerIcon.setTag(Events.ICON_STATE_VIBRATE);
break;
case AudioManager.RINGER_MODE_SILENT:
- mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+ mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
mRingerIcon.setTag(Events.ICON_STATE_MUTE);
addAccessibilityDescription(mRingerIcon, RINGER_MODE_SILENT,
mContext.getString(R.string.volume_ringer_hint_unmute));
@@ -1542,14 +1635,14 @@
default:
boolean muted = (mAutomute && ss.level == 0) || ss.muted;
if (!isZenMuted && muted) {
- mRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
- mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer_mute);
+ mRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
+ mSelectedRingerIcon.setImageResource(mVolumeRingerMuteIconDrawableId);
addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
mContext.getString(R.string.volume_ringer_hint_unmute));
mRingerIcon.setTag(Events.ICON_STATE_MUTE);
} else {
- mRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
- mSelectedRingerIcon.setImageResource(R.drawable.ic_volume_ringer);
+ mRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
+ mSelectedRingerIcon.setImageResource(mVolumeRingerIconDrawableId);
if (mController.hasVibrator()) {
addAccessibilityDescription(mRingerIcon, RINGER_MODE_NORMAL,
mContext.getString(R.string.volume_ringer_hint_vibrate));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
index c5792b9..8f10fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/dagger/VolumeModule.java
@@ -20,6 +20,7 @@
import android.media.AudioManager;
import com.android.internal.jank.InteractionJankMonitor;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.VolumeDialog;
@@ -27,11 +28,14 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxy;
import com.android.systemui.volume.VolumeComponent;
import com.android.systemui.volume.VolumeDialogComponent;
import com.android.systemui.volume.VolumeDialogImpl;
import com.android.systemui.volume.VolumePanelFactory;
+import java.util.concurrent.Executor;
+
import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -55,7 +59,9 @@
MediaOutputDialogFactory mediaOutputDialogFactory,
VolumePanelFactory volumePanelFactory,
ActivityStarter activityStarter,
- InteractionJankMonitor interactionJankMonitor) {
+ InteractionJankMonitor interactionJankMonitor,
+ DeviceConfigProxy deviceConfigProxy,
+ @Main Executor executor) {
VolumeDialogImpl impl = new VolumeDialogImpl(
context,
volumeDialogController,
@@ -65,7 +71,9 @@
mediaOutputDialogFactory,
volumePanelFactory,
activityStarter,
- interactionJankMonitor);
+ interactionJankMonitor,
+ deviceConfigProxy,
+ executor);
impl.setStreamImportant(AudioManager.STREAM_SYSTEM, false);
impl.setAutomute(true);
impl.setSilentMode(false);
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index 78c28ea..d8331ab 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -127,6 +127,12 @@
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true" />
+ <activity android:name=".sensorprivacy.SensorUseStartedActivityTest$SensorUseStartedActivityTestable"
+ android:exported="false"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true" />
+
<provider
android:name="androidx.startup.InitializationProvider"
tools:replace="android:authorities"
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
index 27701be..7a5b772 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/clock/ClockManagerTest.java
@@ -36,8 +36,8 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index eaef159..5e6acd2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -111,6 +111,21 @@
}
@Test
+ fun testCredentialPasswordDismissesOnBack() {
+ val container = initializeCredentialPasswordContainer(addToView = true)
+ assertThat(container.parent).isNotNull()
+ val root = container.rootView
+
+ // Simulate back invocation
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+ waitForIdleSync()
+
+ assertThat(container.parent).isNull()
+ assertThat(root.isAttachedToWindow).isFalse()
+ }
+
+ @Test
fun testIgnoresAnimatedInWhenDismissed() {
val container = initializeFingerprintContainer(addToView = false)
container.dismissFromSystemServer()
@@ -355,20 +370,7 @@
@Test
fun testCredentialUI_disablesClickingOnBackground() {
- whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
- whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
- DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
- )
-
- // In the credential view, clicking on the background (to cancel authentication) is not
- // valid. Thus, the listener should be null, and it should not be in the accessibility
- // hierarchy.
- val container = initializeFingerprintContainer(
- authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
- )
- waitForIdleSync()
-
- assertThat(container.hasCredentialPasswordView()).isTrue()
+ val container = initializeCredentialPasswordContainer()
assertThat(container.hasBiometricPrompt()).isFalse()
assertThat(
container.findViewById<View>(R.id.background)?.isImportantForAccessibility
@@ -428,6 +430,27 @@
verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L)
}
+ private fun initializeCredentialPasswordContainer(
+ addToView: Boolean = true,
+ ): TestAuthContainerView {
+ whenever(userManager.getCredentialOwnerProfile(anyInt())).thenReturn(20)
+ whenever(lockPatternUtils.getKeyguardStoredPasswordQuality(eq(20))).thenReturn(
+ DevicePolicyManager.PASSWORD_QUALITY_NUMERIC
+ )
+
+ // In the credential view, clicking on the background (to cancel authentication) is not
+ // valid. Thus, the listener should be null, and it should not be in the accessibility
+ // hierarchy.
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL,
+ addToView = addToView,
+ )
+ waitForIdleSync()
+
+ assertThat(container.hasCredentialPasswordView()).isTrue()
+ return container
+ }
+
private fun initializeFingerprintContainer(
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
addToView: Boolean = true
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt
index ed16721..1e7b1f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsDebugRestarterTest.kt
@@ -48,22 +48,22 @@
@Test
fun testRestart_ImmediateWhenAsleep() {
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
- restarter.restartSystemUI()
- verify(systemExitRestarter).restartSystemUI()
+ restarter.restart()
+ verify(systemExitRestarter).restart()
}
@Test
fun testRestart_WaitsForSceenOff() {
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
- restarter.restartSystemUI()
- verify(systemExitRestarter, never()).restartSystemUI()
+ restarter.restart()
+ verify(systemExitRestarter, never()).restart()
val captor = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
verify(wakefulnessLifecycle).addObserver(captor.capture())
captor.value.onFinishedGoingToSleep()
- verify(systemExitRestarter).restartSystemUI()
+ verify(systemExitRestarter).restart()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt
index 7d807e2..68ca48d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsReleaseRestarterTest.kt
@@ -63,7 +63,7 @@
whenever(batteryController.isPluggedIn).thenReturn(true)
assertThat(executor.numPending()).isEqualTo(0)
- restarter.restartSystemUI()
+ restarter.restart()
assertThat(executor.numPending()).isEqualTo(1)
}
@@ -72,11 +72,11 @@
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
whenever(batteryController.isPluggedIn).thenReturn(true)
- restarter.restartSystemUI()
- verify(systemExitRestarter, never()).restartSystemUI()
+ restarter.restart()
+ verify(systemExitRestarter, never()).restart()
executor.advanceClockToLast()
executor.runAllReady()
- verify(systemExitRestarter).restartSystemUI()
+ verify(systemExitRestarter).restart()
}
@Test
@@ -85,7 +85,7 @@
whenever(batteryController.isPluggedIn).thenReturn(true)
assertThat(executor.numPending()).isEqualTo(0)
- restarter.restartSystemUI()
+ restarter.restart()
assertThat(executor.numPending()).isEqualTo(0)
}
@@ -95,7 +95,7 @@
whenever(batteryController.isPluggedIn).thenReturn(false)
assertThat(executor.numPending()).isEqualTo(0)
- restarter.restartSystemUI()
+ restarter.restart()
assertThat(executor.numPending()).isEqualTo(0)
}
@@ -105,8 +105,8 @@
whenever(batteryController.isPluggedIn).thenReturn(true)
assertThat(executor.numPending()).isEqualTo(0)
- restarter.restartSystemUI()
- restarter.restartSystemUI()
+ restarter.restart()
+ restarter.restart()
assertThat(executor.numPending()).isEqualTo(1)
}
@@ -115,7 +115,7 @@
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_AWAKE)
whenever(batteryController.isPluggedIn).thenReturn(true)
assertThat(executor.numPending()).isEqualTo(0)
- restarter.restartSystemUI()
+ restarter.restart()
val captor = ArgumentCaptor.forClass(WakefulnessLifecycle.Observer::class.java)
verify(wakefulnessLifecycle).addObserver(captor.capture())
@@ -131,7 +131,7 @@
whenever(wakefulnessLifecycle.wakefulness).thenReturn(WAKEFULNESS_ASLEEP)
whenever(batteryController.isPluggedIn).thenReturn(false)
assertThat(executor.numPending()).isEqualTo(0)
- restarter.restartSystemUI()
+ restarter.restart()
val captor =
ArgumentCaptor.forClass(BatteryController.BatteryStateChangeCallback::class.java)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
index 8395f02..5e27a50 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardQuickAffordanceProviderTest.kt
@@ -99,10 +99,12 @@
setOf(
FakeKeyguardQuickAffordanceConfig(
key = AFFORDANCE_1,
+ pickerName = AFFORDANCE_1_NAME,
pickerIconResourceId = 1,
),
FakeKeyguardQuickAffordanceConfig(
key = AFFORDANCE_2,
+ pickerName = AFFORDANCE_2_NAME,
pickerIconResourceId = 2,
),
),
@@ -176,6 +178,7 @@
runBlocking(IMMEDIATE) {
val slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START
val affordanceId = AFFORDANCE_2
+ val affordanceName = AFFORDANCE_2_NAME
insertSelection(
slotId = slotId,
@@ -188,6 +191,7 @@
Selection(
slotId = slotId,
affordanceId = affordanceId,
+ affordanceName = affordanceName,
)
)
)
@@ -219,12 +223,12 @@
listOf(
Affordance(
id = AFFORDANCE_1,
- name = AFFORDANCE_1,
+ name = AFFORDANCE_1_NAME,
iconResourceId = 1,
),
Affordance(
id = AFFORDANCE_2,
- name = AFFORDANCE_2,
+ name = AFFORDANCE_2_NAME,
iconResourceId = 2,
),
)
@@ -259,6 +263,7 @@
Selection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceId = AFFORDANCE_1,
+ affordanceName = AFFORDANCE_1_NAME,
)
)
)
@@ -290,6 +295,7 @@
Selection(
slotId = KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START,
affordanceId = AFFORDANCE_1,
+ affordanceName = AFFORDANCE_1_NAME,
)
)
)
@@ -323,7 +329,13 @@
cursor.getColumnIndex(Contract.SelectionTable.Columns.SLOT_ID)
val affordanceIdColumnIndex =
cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_ID)
- if (slotIdColumnIndex == -1 || affordanceIdColumnIndex == -1) {
+ val affordanceNameColumnIndex =
+ cursor.getColumnIndex(Contract.SelectionTable.Columns.AFFORDANCE_NAME)
+ if (
+ slotIdColumnIndex == -1 ||
+ affordanceIdColumnIndex == -1 ||
+ affordanceNameColumnIndex == -1
+ ) {
return@buildList
}
@@ -332,6 +344,7 @@
Selection(
slotId = cursor.getString(slotIdColumnIndex),
affordanceId = cursor.getString(affordanceIdColumnIndex),
+ affordanceName = cursor.getString(affordanceNameColumnIndex),
)
)
}
@@ -419,11 +432,14 @@
data class Selection(
val slotId: String,
val affordanceId: String,
+ val affordanceName: String,
)
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
private const val AFFORDANCE_1 = "affordance_1"
private const val AFFORDANCE_2 = "affordance_2"
+ private const val AFFORDANCE_1_NAME = "affordance_1_name"
+ private const val AFFORDANCE_2_NAME = "affordance_2_name"
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 45aaaa2..d17e374 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
+import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserSwitcherController;
@@ -112,6 +113,7 @@
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
private @Mock DreamOverlayStateController mDreamOverlayStateController;
private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+ private @Mock ScrimController mScrimController;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -314,7 +316,8 @@
mDreamOverlayStateController,
() -> mShadeController,
mNotificationShadeWindowControllerLazy,
- () -> mActivityLaunchAnimator);
+ () -> mActivityLaunchAnimator,
+ () -> mScrimController);
mViewMediator.start();
mViewMediator.registerCentralSurfaces(mCentralSurfaces, null, null, null, null, null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 6ba0634..13fc9fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -22,18 +22,25 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
+import com.android.systemui.doze.DozeMachine
+import com.android.systemui.doze.DozeTransitionCallback
+import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -52,6 +59,7 @@
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
@Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
+ @Mock private lateinit var dozeTransitionListener: DozeTransitionListener
private lateinit var underTest: KeyguardRepositoryImpl
@@ -67,272 +75,349 @@
biometricUnlockController,
keyguardStateController,
keyguardUpdateMonitor,
+ dozeTransitionListener,
)
}
@Test
- fun animateBottomAreaDozingTransitions() = runBlockingTest {
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)
+ fun animateBottomAreaDozingTransitions() =
+ runTest(UnconfinedTestDispatcher()) {
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isEqualTo(false)
- underTest.setAnimateDozingTransitions(true)
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
+ underTest.setAnimateDozingTransitions(true)
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
- underTest.setAnimateDozingTransitions(false)
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isFalse()
+ underTest.setAnimateDozingTransitions(false)
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isFalse()
- underTest.setAnimateDozingTransitions(true)
- assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
- }
+ underTest.setAnimateDozingTransitions(true)
+ assertThat(underTest.animateBottomAreaDozingTransitions.value).isTrue()
+ }
@Test
- fun bottomAreaAlpha() = runBlockingTest {
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
+ fun bottomAreaAlpha() =
+ runTest(UnconfinedTestDispatcher()) {
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
- underTest.setBottomAreaAlpha(0.1f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
+ underTest.setBottomAreaAlpha(0.1f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.1f)
- underTest.setBottomAreaAlpha(0.2f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
+ underTest.setBottomAreaAlpha(0.2f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.2f)
- underTest.setBottomAreaAlpha(0.3f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
+ underTest.setBottomAreaAlpha(0.3f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.3f)
- underTest.setBottomAreaAlpha(0.5f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
+ underTest.setBottomAreaAlpha(0.5f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(0.5f)
- underTest.setBottomAreaAlpha(1.0f)
- assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
- }
+ underTest.setBottomAreaAlpha(1.0f)
+ assertThat(underTest.bottomAreaAlpha.value).isEqualTo(1f)
+ }
@Test
- fun clockPosition() = runBlockingTest {
- assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
+ fun clockPosition() =
+ runTest(UnconfinedTestDispatcher()) {
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 0))
- underTest.setClockPosition(0, 1)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
+ underTest.setClockPosition(0, 1)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(0, 1))
- underTest.setClockPosition(1, 9)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
+ underTest.setClockPosition(1, 9)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 9))
- underTest.setClockPosition(1, 0)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
+ underTest.setClockPosition(1, 0)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(1, 0))
- underTest.setClockPosition(3, 1)
- assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
- }
+ underTest.setClockPosition(3, 1)
+ assertThat(underTest.clockPosition.value).isEqualTo(Position(3, 1))
+ }
@Test
- fun isKeyguardShowing() = runBlockingTest {
- whenever(keyguardStateController.isShowing).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
+ fun isKeyguardShowing() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardShowing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
- assertThat(underTest.isKeyguardShowing()).isFalse()
+ assertThat(latest).isFalse()
+ assertThat(underTest.isKeyguardShowing()).isFalse()
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
- whenever(keyguardStateController.isShowing).thenReturn(true)
- captor.value.onKeyguardShowingChanged()
- assertThat(latest).isTrue()
- assertThat(underTest.isKeyguardShowing()).isTrue()
+ whenever(keyguardStateController.isShowing).thenReturn(true)
+ captor.value.onKeyguardShowingChanged()
+ assertThat(latest).isTrue()
+ assertThat(underTest.isKeyguardShowing()).isTrue()
- whenever(keyguardStateController.isShowing).thenReturn(false)
- captor.value.onKeyguardShowingChanged()
- assertThat(latest).isFalse()
- assertThat(underTest.isKeyguardShowing()).isFalse()
+ whenever(keyguardStateController.isShowing).thenReturn(false)
+ captor.value.onKeyguardShowingChanged()
+ assertThat(latest).isFalse()
+ assertThat(underTest.isKeyguardShowing()).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isDozing() = runBlockingTest {
- var latest: Boolean? = null
- val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ fun isDozing() =
+ runTest(UnconfinedTestDispatcher()) {
+ var latest: Boolean? = null
+ val job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- val captor = argumentCaptor<DozeHost.Callback>()
- verify(dozeHost).addCallback(captor.capture())
+ val captor = argumentCaptor<DozeHost.Callback>()
+ verify(dozeHost).addCallback(captor.capture())
- captor.value.onDozingChanged(true)
- assertThat(latest).isTrue()
+ captor.value.onDozingChanged(true)
+ assertThat(latest).isTrue()
- captor.value.onDozingChanged(false)
- assertThat(latest).isFalse()
+ captor.value.onDozingChanged(false)
+ assertThat(latest).isFalse()
- job.cancel()
- verify(dozeHost).removeCallback(captor.value)
- }
+ job.cancel()
+ verify(dozeHost).removeCallback(captor.value)
+ }
@Test
- fun `isDozing - starts with correct initial value for isDozing`() = runBlockingTest {
- var latest: Boolean? = null
+ fun `isDozing - starts with correct initial value for isDozing`() =
+ runTest(UnconfinedTestDispatcher()) {
+ var latest: Boolean? = null
- whenever(statusBarStateController.isDozing).thenReturn(true)
- var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isTrue()
- job.cancel()
+ whenever(statusBarStateController.isDozing).thenReturn(true)
+ var job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isTrue()
+ job.cancel()
- whenever(statusBarStateController.isDozing).thenReturn(false)
- job = underTest.isDozing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
- job.cancel()
- }
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+ job = underTest.isDozing.onEach { latest = it }.launchIn(this)
+ assertThat(latest).isFalse()
+ job.cancel()
+ }
@Test
- fun dozeAmount() = runBlockingTest {
- val values = mutableListOf<Float>()
- val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
+ fun dozeAmount() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
- val captor = argumentCaptor<StatusBarStateController.StateListener>()
- verify(statusBarStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<StatusBarStateController.StateListener>()
+ verify(statusBarStateController).addCallback(captor.capture())
- captor.value.onDozeAmountChanged(0.433f, 0.4f)
- captor.value.onDozeAmountChanged(0.498f, 0.5f)
- captor.value.onDozeAmountChanged(0.661f, 0.65f)
+ captor.value.onDozeAmountChanged(0.433f, 0.4f)
+ captor.value.onDozeAmountChanged(0.498f, 0.5f)
+ captor.value.onDozeAmountChanged(0.661f, 0.65f)
- assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
+ assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
- job.cancel()
- verify(statusBarStateController).removeCallback(captor.value)
- }
+ job.cancel()
+ verify(statusBarStateController).removeCallback(captor.value)
+ }
@Test
- fun wakefulness() = runBlockingTest {
- val values = mutableListOf<WakefulnessModel>()
- val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+ fun wakefulness() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<WakefulnessModel>()
+ val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
- val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
- verify(wakefulnessLifecycle).addObserver(captor.capture())
+ val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
+ verify(wakefulnessLifecycle).addObserver(captor.capture())
- captor.value.onStartedWakingUp()
- captor.value.onFinishedWakingUp()
- captor.value.onStartedGoingToSleep()
- captor.value.onFinishedGoingToSleep()
+ captor.value.onStartedWakingUp()
+ captor.value.onFinishedWakingUp()
+ captor.value.onStartedGoingToSleep()
+ captor.value.onFinishedGoingToSleep()
- assertThat(values)
- .isEqualTo(
- listOf(
- // Initial value will be ASLEEP
- WakefulnessModel.ASLEEP,
- WakefulnessModel.STARTING_TO_WAKE,
- WakefulnessModel.AWAKE,
- WakefulnessModel.STARTING_TO_SLEEP,
- WakefulnessModel.ASLEEP,
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be ASLEEP
+ WakefulnessModel.ASLEEP,
+ WakefulnessModel.STARTING_TO_WAKE,
+ WakefulnessModel.AWAKE,
+ WakefulnessModel.STARTING_TO_SLEEP,
+ WakefulnessModel.ASLEEP,
+ )
)
- )
- job.cancel()
- verify(wakefulnessLifecycle).removeObserver(captor.value)
- }
+ job.cancel()
+ verify(wakefulnessLifecycle).removeObserver(captor.value)
+ }
@Test
- fun isUdfpsSupported() = runBlockingTest {
- whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
- assertThat(underTest.isUdfpsSupported()).isTrue()
+ fun isUdfpsSupported() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(true)
+ assertThat(underTest.isUdfpsSupported()).isTrue()
- whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
- assertThat(underTest.isUdfpsSupported()).isFalse()
- }
+ whenever(keyguardUpdateMonitor.isUdfpsSupported).thenReturn(false)
+ assertThat(underTest.isUdfpsSupported()).isFalse()
+ }
@Test
- fun isBouncerShowing() = runBlockingTest {
- whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
+ fun isBouncerShowing() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
- whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
- captor.value.onBouncerShowingChanged()
- assertThat(latest).isTrue()
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
+ captor.value.onBouncerShowingChanged()
+ assertThat(latest).isTrue()
- whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
- captor.value.onBouncerShowingChanged()
- assertThat(latest).isFalse()
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
+ captor.value.onBouncerShowingChanged()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isKeyguardGoingAway() = runBlockingTest {
- whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
+ fun isKeyguardGoingAway() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isKeyguardGoingAway.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- val captor = argumentCaptor<KeyguardStateController.Callback>()
- verify(keyguardStateController).addCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
- whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
- captor.value.onKeyguardGoingAwayChanged()
- assertThat(latest).isTrue()
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(true)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isTrue()
- whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
- captor.value.onKeyguardGoingAwayChanged()
- assertThat(latest).isFalse()
+ whenever(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
+ captor.value.onKeyguardGoingAwayChanged()
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun isDreaming() = runBlockingTest {
- whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
- var latest: Boolean? = null
- val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
+ fun isDreaming() =
+ runTest(UnconfinedTestDispatcher()) {
+ whenever(keyguardUpdateMonitor.isDreaming()).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isDreaming.onEach { latest = it }.launchIn(this)
- assertThat(latest).isFalse()
+ assertThat(latest).isFalse()
- val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
- verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
- captor.value.onDreamingStateChanged(true)
- assertThat(latest).isTrue()
+ captor.value.onDreamingStateChanged(true)
+ assertThat(latest).isTrue()
- captor.value.onDreamingStateChanged(false)
- assertThat(latest).isFalse()
+ captor.value.onDreamingStateChanged(false)
+ assertThat(latest).isFalse()
- job.cancel()
- }
+ job.cancel()
+ }
@Test
- fun biometricUnlockState() = runBlockingTest {
- val values = mutableListOf<BiometricUnlockModel>()
- val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
+ fun biometricUnlockState() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<BiometricUnlockModel>()
+ val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
- val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
- verify(biometricUnlockController).addBiometricModeListener(captor.capture())
+ val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
+ verify(biometricUnlockController).addBiometricModeListener(captor.capture())
- captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
- assertThat(values)
- .isEqualTo(
- listOf(
- // Initial value will be NONE, followed by onModeChanged() call
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.NONE,
- BiometricUnlockModel.WAKE_AND_UNLOCK,
- BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
- BiometricUnlockModel.SHOW_BOUNCER,
- BiometricUnlockModel.ONLY_WAKE,
- BiometricUnlockModel.UNLOCK_COLLAPSING,
- BiometricUnlockModel.DISMISS_BOUNCER,
- BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be NONE, followed by onModeChanged() call
+ BiometricUnlockModel.NONE,
+ BiometricUnlockModel.NONE,
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.SHOW_BOUNCER,
+ BiometricUnlockModel.ONLY_WAKE,
+ BiometricUnlockModel.UNLOCK_COLLAPSING,
+ BiometricUnlockModel.DISMISS_BOUNCER,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
+ )
)
+
+ job.cancel()
+ verify(biometricUnlockController).removeBiometricModeListener(captor.value)
+ }
+
+ @Test
+ fun dozeTransitionModel() =
+ runTest(UnconfinedTestDispatcher()) {
+ // For the initial state
+ whenever(dozeTransitionListener.oldState).thenReturn(DozeMachine.State.UNINITIALIZED)
+ whenever(dozeTransitionListener.newState).thenReturn(DozeMachine.State.UNINITIALIZED)
+
+ val values = mutableListOf<DozeTransitionModel>()
+ val job = underTest.dozeTransitionModel.onEach(values::add).launchIn(this)
+
+ val listener =
+ withArgCaptor<DozeTransitionCallback> {
+ verify(dozeTransitionListener).addCallback(capture())
+ }
+
+ // These don't have to reflect real transitions from the DozeMachine. Only that the
+ // transitions are properly emitted
+ listener.onDozeTransition(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE)
+ listener.onDozeTransition(DozeMachine.State.DOZE, DozeMachine.State.DOZE_AOD)
+ listener.onDozeTransition(DozeMachine.State.DOZE_AOD_DOCKED, DozeMachine.State.FINISH)
+ listener.onDozeTransition(
+ DozeMachine.State.DOZE_REQUEST_PULSE,
+ DozeMachine.State.DOZE_PULSING
+ )
+ listener.onDozeTransition(
+ DozeMachine.State.DOZE_SUSPEND_TRIGGERS,
+ DozeMachine.State.DOZE_PULSE_DONE
+ )
+ listener.onDozeTransition(
+ DozeMachine.State.DOZE_AOD_PAUSING,
+ DozeMachine.State.DOZE_AOD_PAUSED
)
- job.cancel()
- verify(biometricUnlockController).removeBiometricModeListener(captor.value)
- }
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be UNINITIALIZED
+ DozeTransitionModel(
+ DozeStateModel.UNINITIALIZED,
+ DozeStateModel.UNINITIALIZED
+ ),
+ DozeTransitionModel(DozeStateModel.INITIALIZED, DozeStateModel.DOZE),
+ DozeTransitionModel(DozeStateModel.DOZE, DozeStateModel.DOZE_AOD),
+ DozeTransitionModel(DozeStateModel.DOZE_AOD_DOCKED, DozeStateModel.FINISH),
+ DozeTransitionModel(
+ DozeStateModel.DOZE_REQUEST_PULSE,
+ DozeStateModel.DOZE_PULSING
+ ),
+ DozeTransitionModel(
+ DozeStateModel.DOZE_SUSPEND_TRIGGERS,
+ DozeStateModel.DOZE_PULSE_DONE
+ ),
+ DozeTransitionModel(
+ DozeStateModel.DOZE_AOD_PAUSING,
+ DozeStateModel.DOZE_AOD_PAUSED
+ ),
+ )
+ )
+
+ job.cancel()
+ verify(dozeTransitionListener).removeCallback(listener)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index c47e6f5..4850ea5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -33,6 +33,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.model.KeyguardQuickAffordanceModel
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
+import com.android.systemui.keyguard.shared.model.KeyguardQuickAffordancePickerRepresentation
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.plugins.ActivityStarter
@@ -314,7 +315,13 @@
.isEqualTo(
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
- listOf(homeControls.key),
+ listOf(
+ KeyguardQuickAffordancePickerRepresentation(
+ id = homeControls.key,
+ name = homeControls.pickerName,
+ iconResourceId = homeControls.pickerIconResourceId,
+ ),
+ ),
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
)
)
@@ -343,7 +350,13 @@
.isEqualTo(
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
- listOf(quickAccessWallet.key),
+ listOf(
+ KeyguardQuickAffordancePickerRepresentation(
+ id = quickAccessWallet.key,
+ name = quickAccessWallet.pickerName,
+ iconResourceId = quickAccessWallet.pickerIconResourceId,
+ ),
+ ),
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to emptyList(),
)
)
@@ -375,9 +388,21 @@
.isEqualTo(
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to
- listOf(quickAccessWallet.key),
+ listOf(
+ KeyguardQuickAffordancePickerRepresentation(
+ id = quickAccessWallet.key,
+ name = quickAccessWallet.pickerName,
+ iconResourceId = quickAccessWallet.pickerIconResourceId,
+ ),
+ ),
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
- listOf(qrCodeScanner.key),
+ listOf(
+ KeyguardQuickAffordancePickerRepresentation(
+ id = qrCodeScanner.key,
+ name = qrCodeScanner.pickerName,
+ iconResourceId = qrCodeScanner.pickerIconResourceId,
+ ),
+ ),
)
)
@@ -441,7 +466,13 @@
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
- listOf(quickAccessWallet.key),
+ listOf(
+ KeyguardQuickAffordancePickerRepresentation(
+ id = quickAccessWallet.key,
+ name = quickAccessWallet.pickerName,
+ iconResourceId = quickAccessWallet.pickerIconResourceId,
+ ),
+ ),
)
)
@@ -502,7 +533,13 @@
mapOf(
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_START to emptyList(),
KeyguardQuickAffordanceSlots.SLOT_ID_BOTTOM_END to
- listOf(quickAccessWallet.key),
+ listOf(
+ KeyguardQuickAffordancePickerRepresentation(
+ id = quickAccessWallet.key,
+ name = quickAccessWallet.pickerName,
+ iconResourceId = quickAccessWallet.pickerIconResourceId,
+ ),
+ ),
)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
index 3269f5a..559f183 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractorTest.kt
@@ -43,6 +43,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Answers
+import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
@@ -170,8 +171,10 @@
@Test
fun testShowMessage() {
+ val argCaptor = ArgumentCaptor.forClass(BouncerShowMessageModel::class.java)
mPrimaryBouncerInteractor.showMessage("abc", null)
- verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
+ verify(repository).setShowMessage(argCaptor.capture())
+ assertThat(argCaptor.value.message).isEqualTo("abc")
}
@Test
@@ -195,6 +198,12 @@
}
@Test
+ fun testNotifyShowedMessage() {
+ mPrimaryBouncerInteractor.onMessageShown()
+ verify(repository).setShowMessage(null)
+ }
+
+ @Test
fun testOnScreenTurnedOff() {
mPrimaryBouncerInteractor.onScreenTurnedOff()
verify(repository).setOnScreenTurnedOff(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
new file mode 100644
index 0000000..3727134
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModelTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2022 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardBouncerViewModelTest : SysuiTestCase() {
+ lateinit var underTest: KeyguardBouncerViewModel
+ @Mock lateinit var bouncerView: BouncerView
+ @Mock lateinit var bouncerInteractor: PrimaryBouncerInteractor
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
+ }
+
+ @Test
+ fun setMessage() =
+ runBlocking(Dispatchers.Main.immediate) {
+ val flow = MutableStateFlow<BouncerShowMessageModel?>(null)
+ var message: BouncerShowMessageModel? = null
+ Mockito.`when`(bouncerInteractor.showMessage)
+ .thenReturn(flow as Flow<BouncerShowMessageModel>)
+ // Reinitialize the view model.
+ underTest = KeyguardBouncerViewModel(bouncerView, bouncerInteractor)
+
+ flow.value = BouncerShowMessageModel(message = "abc", colorStateList = null)
+
+ val job = underTest.bouncerShowMessage.onEach { message = it }.launchIn(this)
+ assertThat(message?.message).isEqualTo("abc")
+ job.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index c452872..fb1a720 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -54,6 +54,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.nano.SystemUIProtoDump;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -66,7 +67,6 @@
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarIconController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 213eca8..25c95ef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -42,12 +42,12 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
import com.android.systemui.settings.UserFileManager;
import com.android.systemui.settings.UserTracker;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.CentralSurfaces;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
new file mode 100644
index 0000000..333e634
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/sensorprivacy/SensorUseStartedActivityTest.kt
@@ -0,0 +1,38 @@
+package com.android.systemui.sensorprivacy
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class SensorUseStartedActivityTest : SysuiTestCase() {
+ open class SensorUseStartedActivityTestable :
+ SensorUseStartedActivity(
+ sensorPrivacyController = mock(),
+ keyguardStateController = mock(),
+ keyguardDismissUtil = mock(),
+ bgHandler = mock(),
+ )
+
+ @get:Rule val activityRule = ActivityScenarioRule(SensorUseStartedActivityTestable::class.java)
+
+ @Test
+ fun onBackPressed_doNothing() {
+ activityRule.scenario.onActivity { activity ->
+ assertThat(activity.isFinishing).isFalse()
+
+ activity.onBackPressed()
+
+ assertThat(activity.isFinishing).isFalse()
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
index bc17c19..9c36be6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/CombinedShadeHeaderConstraintsTest.kt
@@ -43,7 +43,7 @@
load(context, context.resources.getXml(R.xml.qqs_header))
}
qsConstraint = ConstraintSet().apply {
- load(context, context.resources.getXml(R.xml.qs_header_new))
+ load(context, context.resources.getXml(R.xml.qs_header))
}
largeScreenConstraint = ConstraintSet().apply {
load(context, context.resources.getXml(R.xml.large_screen_shade_header))
@@ -344,26 +344,6 @@
}
@Test
- fun testCheckViewsDontChangeSizeBetweenAnimationConstraints() {
- val views = mapOf(
- R.id.clock to "clock",
- R.id.date to "date",
- R.id.statusIcons to "icons",
- R.id.privacy_container to "privacy",
- R.id.carrier_group to "carriers",
- R.id.batteryRemainingIcon to "battery",
- )
- views.forEach { (id, name) ->
- assertWithMessage("$name changes height")
- .that(qqsConstraint.getConstraint(id).layout.mHeight)
- .isEqualTo(qsConstraint.getConstraint(id).layout.mHeight)
- assertWithMessage("$name changes width")
- .that(qqsConstraint.getConstraint(id).layout.mWidth)
- .isEqualTo(qsConstraint.getConstraint(id).layout.mWidth)
- }
- }
-
- @Test
fun testEmptyCutoutDateIconsAreConstrainedWidth() {
CombinedShadeHeadersConstraintManagerImpl.emptyCutoutConstraints()()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
index 14a3bc1..e1007fa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/LargeScreenShadeHeaderControllerCombinedTest.kt
@@ -179,7 +179,6 @@
whenever(iconManagerFactory.create(any(), any())).thenReturn(iconManager)
whenever(featureFlags.isEnabled(Flags.COMBINED_QS_HEADERS)).thenReturn(true)
- whenever(featureFlags.isEnabled(Flags.NEW_HEADER)).thenReturn(true)
setUpDefaultInsets()
setUpMotionLayout(view)
@@ -212,7 +211,7 @@
assertThat(captor.value.getResId()).isEqualTo(R.xml.qqs_header)
verify(qsConstraints).load(eq(context), capture(captor))
- assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header_new)
+ assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header)
verify(largeScreenConstraints).load(eq(context), capture(captor))
assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 28bd26a..4d7741ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -28,7 +28,7 @@
import com.android.systemui.plugins.ClockMetadata
import com.android.systemui.plugins.ClockProviderPlugin
import com.android.systemui.plugins.PluginListener
-import com.android.systemui.shared.plugins.PluginManager
+import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.eq
import junit.framework.Assert.assertEquals
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
index 5e11858..3412679 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationListenerTest.java
@@ -37,7 +37,7 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.NotificationListener.NotificationHandler;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
index 3b05321..94e3e6c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifCollectionTest.java
@@ -92,6 +92,7 @@
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionLogger;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -741,22 +742,24 @@
@Test
public void testGroupChildrenAreDismissedLocallyWhenSummaryIsDismissed() {
// GIVEN a collection with two grouped notifs in it
- CollectionEvent notif0 = postNotif(
+ CollectionEvent groupNotif = postNotif(
buildNotif(TEST_PACKAGE, 0)
.setGroup(mContext, GROUP_1)
.setGroupSummary(mContext, true));
- CollectionEvent notif1 = postNotif(
+ CollectionEvent childNotif = postNotif(
buildNotif(TEST_PACKAGE, 1)
.setGroup(mContext, GROUP_1));
- NotificationEntry entry0 = mCollectionListener.getEntry(notif0.key);
- NotificationEntry entry1 = mCollectionListener.getEntry(notif1.key);
+ NotificationEntry groupEntry = mCollectionListener.getEntry(groupNotif.key);
+ NotificationEntry childEntry = mCollectionListener.getEntry(childNotif.key);
+ ExpandableNotificationRow childRow = mock(ExpandableNotificationRow.class);
+ childEntry.setRow(childRow);
// WHEN the summary is dismissed
- mCollection.dismissNotification(entry0, defaultStats(entry0));
+ mCollection.dismissNotification(groupEntry, defaultStats(groupEntry));
// THEN all members of the group are marked as dismissed locally
- assertEquals(DISMISSED, entry0.getDismissState());
- assertEquals(PARENT_DISMISSED, entry1.getDismissState());
+ assertEquals(DISMISSED, groupEntry.getDismissState());
+ assertEquals(PARENT_DISMISSED, childEntry.getDismissState());
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
index 15cf17d..6167b46 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferTest.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.testing.AndroidTestingRunner
import android.view.View
+import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
@@ -26,6 +27,10 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.isNull
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.matches
+import org.mockito.Mockito.verify
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -124,6 +129,64 @@
Assert.assertNull(controller3.view.parent)
Assert.assertNull(controller4.view.parent)
Assert.assertNull(controller5.view.parent)
+ verifyDetachingChildLogged(controller3, oldParent = controller2)
+ verifyDetachingChildLogged(controller4, oldParent = controller2)
+ verifyDetachingChildLogged(controller5, oldParent = controller2)
+ }
+
+ @Test
+ fun testRemovedGroupsWithKeepInParentAreKeptTogether() {
+ // GIVEN a preexisting tree with a group
+ // AND the group children supports keepInParent
+ applySpecAndCheck(
+ node(controller1),
+ node(controller2, node(controller3), node(controller4), node(controller5))
+ )
+ controller3.supportsKeepInParent = true
+ controller4.supportsKeepInParent = true
+ controller5.supportsKeepInParent = true
+
+ // WHEN the new spec removes the entire group
+ applySpecAndCheck(node(controller1))
+
+ // THEN the group children are still attached to their parent
+ Assert.assertEquals(controller2.view, controller3.view.parent)
+ Assert.assertEquals(controller2.view, controller4.view.parent)
+ Assert.assertEquals(controller2.view, controller5.view.parent)
+ verifySkipDetachingChildLogged(controller3, parent = controller2)
+ verifySkipDetachingChildLogged(controller4, parent = controller2)
+ verifySkipDetachingChildLogged(controller5, parent = controller2)
+ }
+
+ @Test
+ fun testReuseRemovedGroupsWithKeepInParent() {
+ // GIVEN a preexisting tree with a dismissed group
+ // AND the group children supports keepInParent
+ controller3.supportsKeepInParent = true
+ controller4.supportsKeepInParent = true
+ controller5.supportsKeepInParent = true
+ applySpecAndCheck(
+ node(controller1),
+ node(controller2, node(controller3), node(controller4), node(controller5))
+ )
+ applySpecAndCheck(node(controller1))
+
+ // WHEN a new spec is applied which reuses the dismissed views
+ applySpecAndCheck(
+ node(controller1),
+ node(controller2),
+ node(controller3),
+ node(controller4),
+ node(controller5)
+ )
+
+ // THEN the dismissed views can be reused
+ Assert.assertEquals(rootController.view, controller3.view.parent)
+ Assert.assertEquals(rootController.view, controller4.view.parent)
+ Assert.assertEquals(rootController.view, controller5.view.parent)
+ verifyDetachingChildLogged(controller3, oldParent = null)
+ verifyDetachingChildLogged(controller4, oldParent = null)
+ verifyDetachingChildLogged(controller5, oldParent = null)
}
@Test
@@ -184,7 +247,30 @@
}
}
+ private fun verifySkipDetachingChildLogged(child: NodeController, parent: NodeController) {
+ verify(logger)
+ .logSkipDetachingChild(
+ key = matches(child.nodeLabel),
+ parentKey = matches(parent.nodeLabel),
+ anyBoolean(),
+ anyBoolean()
+ )
+ }
+
+ private fun verifyDetachingChildLogged(child: NodeController, oldParent: NodeController?) {
+ verify(logger)
+ .logDetachingChild(
+ key = matches(child.nodeLabel),
+ isTransfer = anyBoolean(),
+ isParentRemoved = anyBoolean(),
+ oldParent = oldParent?.let { matches(it.nodeLabel) } ?: isNull(),
+ newParent = isNull()
+ )
+ }
+
private class FakeController(context: Context, label: String) : NodeController {
+ var supportsKeepInParent: Boolean = false
+
override val view: FrameLayout = FrameLayout(context)
override val nodeLabel: String = label
override fun getChildCount(): Int = view.childCount
@@ -209,6 +295,22 @@
override fun onViewAdded() {}
override fun onViewMoved() {}
override fun onViewRemoved() {}
+ override fun offerToKeepInParentForAnimation(): Boolean {
+ return supportsKeepInParent
+ }
+
+ override fun removeFromParentIfKeptForAnimation(): Boolean {
+ if (supportsKeepInParent) {
+ (view.parent as? ViewGroup)?.removeView(view)
+ return true
+ }
+
+ return false
+ }
+
+ override fun resetKeepInParentForAnimation() {
+ supportsKeepInParent = false
+ }
}
private class SpecBuilder(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index 12cc114..ee8db18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.app.Notification;
@@ -458,4 +459,79 @@
verify(mNotificationTestHelper.mOnUserInteractionCallback, never())
.registerFutureDismissal(any(), anyInt());
}
+
+ @Test
+ public void testAddChildNotification() throws Exception {
+ ExpandableNotificationRow group = mNotificationTestHelper.createGroup(0);
+ ExpandableNotificationRow child = mNotificationTestHelper.createRow();
+
+ group.addChildNotification(child);
+
+ Assert.assertEquals(child, group.getChildNotificationAt(0));
+ Assert.assertEquals(group, child.getNotificationParent());
+ Assert.assertTrue(child.isChildInGroup());
+ }
+
+ @Test
+ public void testAddChildNotification_childSkipped() throws Exception {
+ ExpandableNotificationRow group = mNotificationTestHelper.createGroup(0);
+ ExpandableNotificationRow child = mNotificationTestHelper.createRow();
+ child.setKeepInParentForDismissAnimation(true);
+
+ group.addChildNotification(child);
+
+ Assert.assertTrue(group.getAttachedChildren().isEmpty());
+ Assert.assertNotEquals(group, child.getNotificationParent());
+ verify(mNotificationTestHelper.getMockLogger()).logSkipAttachingKeepInParentChild(
+ /*child=*/ child.getEntry(),
+ /*newParent=*/ group.getEntry()
+ );
+ }
+
+ @Test
+ public void testRemoveChildNotification() throws Exception {
+ ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1);
+ ExpandableNotificationRow child = group.getAttachedChildren().get(0);
+ child.setKeepInParentForDismissAnimation(true);
+
+ group.removeChildNotification(child);
+
+ Assert.assertNull(child.getParent());
+ Assert.assertNull(child.getNotificationParent());
+ Assert.assertFalse(child.keepInParentForDismissAnimation());
+ verifyNoMoreInteractions(mNotificationTestHelper.getMockLogger());
+ }
+
+ @Test
+ public void testRemoveChildrenWithKeepInParent_removesChildWithKeepInParent() throws Exception {
+ ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1);
+ ExpandableNotificationRow child = group.getAttachedChildren().get(0);
+ child.setKeepInParentForDismissAnimation(true);
+
+ group.removeChildrenWithKeepInParent();
+
+ Assert.assertNull(child.getParent());
+ Assert.assertNull(child.getNotificationParent());
+ Assert.assertFalse(child.keepInParentForDismissAnimation());
+ verify(mNotificationTestHelper.getMockLogger()).logKeepInParentChildDetached(
+ /*child=*/ child.getEntry(),
+ /*oldParent=*/ group.getEntry()
+ );
+ }
+
+ @Test
+ public void testRemoveChildrenWithKeepInParent_skipsChildrenWithoutKeepInParent()
+ throws Exception {
+ ExpandableNotificationRow group = mNotificationTestHelper.createGroup(1);
+ ExpandableNotificationRow child = group.getAttachedChildren().get(0);
+
+ group.removeChildrenWithKeepInParent();
+
+ Assert.assertEquals(group, child.getNotificationParent());
+ Assert.assertFalse(child.keepInParentForDismissAnimation());
+ verify(mNotificationTestHelper.getMockLogger(), never()).logKeepInParentChildDetached(
+ /*child=*/ any(),
+ /*oldParent=*/ any()
+ );
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index ab4ae6a..5fd9448 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -73,7 +73,7 @@
import com.android.systemui.statusbar.notification.icon.IconBuilder;
import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
-import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpansionLogger;
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.ExpandableNotificationRowLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow.OnExpandClickListener;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
@@ -116,6 +116,7 @@
private final Context mContext;
private final TestableLooper mTestLooper;
private int mId;
+ private final ExpandableNotificationRowLogger mMockLogger;
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
private ExpandableNotificationRow mRow;
@@ -139,6 +140,7 @@
dependency.injectMockDependency(NotificationMediaManager.class);
dependency.injectMockDependency(NotificationShadeWindowController.class);
dependency.injectMockDependency(MediaOutputDialogFactory.class);
+ mMockLogger = mock(ExpandableNotificationRowLogger.class);
mStatusBarStateController = mock(StatusBarStateController.class);
mGroupMembershipManager = mock(GroupMembershipManager.class);
mGroupExpansionManager = mock(GroupExpansionManager.class);
@@ -197,6 +199,10 @@
mDefaultInflationFlags = defaultInflationFlags;
}
+ public ExpandableNotificationRowLogger getMockLogger() {
+ return mMockLogger;
+ }
+
/**
* Creates a generic row with rounded border.
*
@@ -527,7 +533,7 @@
mock(RemoteInputViewSubcomponent.Factory.class),
APP_NAME,
entry.getKey(),
- mock(ExpansionLogger.class),
+ mMockLogger,
mock(KeyguardBypassController.class),
mGroupMembershipManager,
mGroupExpansionManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 41912f5..013e727 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -109,6 +109,7 @@
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.PluginDependencyProvider;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.ScreenPinningRequest;
import com.android.systemui.settings.brightness.BrightnessSliderController;
@@ -120,7 +121,6 @@
import com.android.systemui.shade.ShadeController;
import com.android.systemui.shade.ShadeControllerImpl;
import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.KeyguardIndicationController;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
@@ -355,7 +355,6 @@
when(mStackScrollerController.getView()).thenReturn(mStackScroller);
when(mStackScroller.generateLayoutParams(any())).thenReturn(new LayoutParams(0, 0));
- when(mNotificationPanelViewController.getView()).thenReturn(mNotificationPanelView);
when(mNotificationPanelView.getLayoutParams()).thenReturn(new LayoutParams(0, 0));
when(powerManagerService.isInteractive()).thenReturn(true);
when(mStackScroller.getActivatedChild()).thenReturn(null);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index 320a083..e2843a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -76,7 +76,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- `when`(notificationPanelViewController.view).thenReturn(panelView)
`when`(sysuiUnfoldComponent.getStatusBarMoveFromCenterAnimationController())
.thenReturn(moveFromCenterAnimation)
// create the view and controller on main thread as it requires main looper
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
index 5aa7f92..27b1da0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewTest.kt
@@ -25,7 +25,6 @@
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -41,9 +40,6 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- // TODO(b/197137564): Setting up a panel view and its controller feels unnecessary when
- // testing just [PhoneStatusBarView].
- `when`(notificationPanelViewController.view).thenReturn(panelView)
view = PhoneStatusBarView(mContext, null)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 9f70565..bf5186b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -307,6 +307,23 @@
}
@Test
+ public void onPanelExpansionChanged_neverTranslatesBouncerWhenShowBouncer() {
+ // Since KeyguardBouncer.EXPANSION_VISIBLE = 0 panel expansion, if the unlock is dismissing
+ // the bouncer, there may be an onPanelExpansionChanged(0) call to collapse the panel
+ // which would mistakenly cause the bouncer to show briefly before its visibility
+ // is set to hide. Therefore, we don't want to propagate panelExpansionChanged to the
+ // bouncer if the bouncer is dismissing as a result of a biometric unlock.
+ when(mBiometricUnlockController.getMode())
+ .thenReturn(BiometricUnlockController.MODE_SHOW_BOUNCER);
+ mStatusBarKeyguardViewManager.onPanelExpansionChanged(
+ expansionEvent(
+ /* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
+ /* expanded= */ true,
+ /* tracking= */ false));
+ verify(mPrimaryBouncer, never()).setExpansion(anyFloat());
+ }
+
+ @Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenShadeLocked() {
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
@@ -570,4 +587,40 @@
mStatusBarKeyguardViewManager.hideBouncer(false);
verify(mPrimaryBouncerInteractor, never()).hide();
}
+
+ @Test
+ public void hideAlternateBouncer_beforeCentralSurfacesRegistered() {
+ mStatusBarKeyguardViewManager =
+ new StatusBarKeyguardViewManager(
+ getContext(),
+ mViewMediatorCallback,
+ mLockPatternUtils,
+ mStatusBarStateController,
+ mock(ConfigurationController.class),
+ mKeyguardUpdateMonitor,
+ mDreamOverlayStateController,
+ mock(NavigationModeController.class),
+ mock(DockManager.class),
+ mock(NotificationShadeWindowController.class),
+ mKeyguardStateController,
+ mock(NotificationMediaManager.class),
+ mKeyguardBouncerFactory,
+ mKeyguardMessageAreaFactory,
+ Optional.of(mSysUiUnfoldComponent),
+ () -> mShadeController,
+ mLatencyTracker,
+ mKeyguardSecurityModel,
+ mFeatureFlags,
+ mPrimaryBouncerCallbackInteractor,
+ mPrimaryBouncerInteractor,
+ mBouncerView) {
+ @Override
+ public ViewRootImpl getViewRootImpl() {
+ return mViewRootImpl;
+ }
+ };
+
+ // the following call before registering centralSurfaces should NOT throw a NPE:
+ mStatusBarKeyguardViewManager.hideAlternateBouncer(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
index 14cc032..71ac7c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java
@@ -33,7 +33,7 @@
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.statusbar.policy.ExtensionController.Extension;
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 797f86a..27957ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -62,7 +62,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.CommandQueue;
import org.junit.Before;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index 8645298..89402de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -88,6 +88,7 @@
deviceStates = FoldableTestUtils.findDeviceStates(context)
+ // TODO(b/254878364): remove this call to NPVC.getView()
whenever(notificationPanelViewController.view).thenReturn(viewGroup)
whenever(viewGroup.viewTreeObserver).thenReturn(viewTreeObserver)
whenever(wakefulnessLifecycle.lastSleepReason)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
index fc2a78a..e1e54a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt
@@ -15,14 +15,13 @@
*/
package com.android.systemui.unfold.util
-import android.animation.ValueAnimator
import android.content.ContentResolver
import android.database.ContentObserver
+import android.provider.Settings
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.unfold.TestUnfoldTransitionProvider
-import com.android.systemui.unfold.UnfoldTransitionProgressProvider
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.util.mockito.any
import org.junit.Before
@@ -30,6 +29,7 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.MockitoAnnotations
@@ -38,30 +38,25 @@
@SmallTest
class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() {
- @Mock
- lateinit var contentResolver: ContentResolver
-
- @Mock
- lateinit var sinkProvider: TransitionProgressListener
+ @Mock lateinit var sinkProvider: TransitionProgressListener
private val sourceProvider = TestUnfoldTransitionProvider()
- lateinit var progressProvider: ScaleAwareTransitionProgressProvider
+ private lateinit var contentResolver: ContentResolver
+ private lateinit var progressProvider: ScaleAwareTransitionProgressProvider
private val animatorDurationScaleListenerCaptor =
- ArgumentCaptor.forClass(ContentObserver::class.java)
+ ArgumentCaptor.forClass(ContentObserver::class.java)
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ contentResolver = spy(context.contentResolver)
- progressProvider = ScaleAwareTransitionProgressProvider(
- sourceProvider,
- contentResolver
- )
+ progressProvider = ScaleAwareTransitionProgressProvider(sourceProvider, contentResolver)
- verify(contentResolver).registerContentObserver(any(), any(),
- animatorDurationScaleListenerCaptor.capture())
+ verify(contentResolver)
+ .registerContentObserver(any(), any(), animatorDurationScaleListenerCaptor.capture())
progressProvider.addCallback(sinkProvider)
}
@@ -121,12 +116,20 @@
}
private fun setAnimationsEnabled(enabled: Boolean) {
- val durationScale = if (enabled) {
- 1f
- } else {
- 0f
- }
- ValueAnimator.setDurationScale(durationScale)
+ val durationScale =
+ if (enabled) {
+ 1f
+ } else {
+ 0f
+ }
+
+ // It uses [TestableSettingsProvider] and it will be cleared after the test
+ Settings.Global.putString(
+ contentResolver,
+ Settings.Global.ANIMATOR_DURATION_SCALE,
+ durationScale.toString()
+ )
+
animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
index 4b49420..47efcd9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/UserInteractorTest.kt
@@ -771,6 +771,41 @@
)
}
+ @Test
+ fun `users - secondary user - managed profile is not included`() =
+ runBlocking(IMMEDIATE) {
+ var userInfos = createUserInfos(count = 3, includeGuest = false).toMutableList()
+ userInfos.add(
+ UserInfo(
+ 50,
+ "Work Profile",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_MANAGED_PROFILE
+ )
+ )
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true))
+
+ var res: List<UserModel>? = null
+ val job = underTest.users.onEach { res = it }.launchIn(this)
+ assertThat(res?.size == 3).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun `current user is not primary and user switcher is disabled`() =
+ runBlocking(IMMEDIATE) {
+ val userInfos = createUserInfos(count = 2, includeGuest = false)
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+ userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false))
+ var selectedUser: UserModel? = null
+ val job = underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
+ assertThat(selectedUser).isNotNull()
+ job.cancel()
+ }
+
private fun assertUsers(
models: List<UserModel>?,
count: Int,
@@ -893,9 +928,9 @@
name,
/* iconPath= */ "",
/* flags= */ if (isPrimary) {
- UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
+ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL
} else {
- 0
+ UserInfo.FLAG_FULL
},
if (isGuest) {
UserManager.USER_TYPE_FULL_GUEST
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
index db348b80..795ff17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
@@ -281,7 +281,7 @@
USER_ID_0,
USER_NAME_0.text!!,
/* iconPath */ "",
- /* flags */ 0,
+ /* flags */ UserInfo.FLAG_FULL,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
@@ -290,7 +290,7 @@
USER_ID_1,
USER_NAME_1.text!!,
/* iconPath */ "",
- /* flags */ 0,
+ /* flags */ UserInfo.FLAG_FULL,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
@@ -299,7 +299,7 @@
USER_ID_2,
USER_NAME_2.text!!,
/* iconPath */ "",
- /* flags */ 0,
+ /* flags */ UserInfo.FLAG_FULL,
/* userType */ UserManager.USER_TYPE_FULL_SYSTEM
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index eac7fc2..1730b75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -178,21 +178,21 @@
/* id= */ 0,
/* name= */ "zero",
/* iconPath= */ "",
- /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN,
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
),
UserInfo(
/* id= */ 1,
/* name= */ "one",
/* iconPath= */ "",
- /* flags= */ 0,
+ /* flags= */ UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
),
UserInfo(
/* id= */ 2,
/* name= */ "two",
/* iconPath= */ "",
- /* flags= */ 0,
+ /* flags= */ UserInfo.FLAG_FULL,
UserManager.USER_TYPE_FULL_SYSTEM,
),
)
@@ -361,10 +361,10 @@
/* iconPath= */ "",
/* flags= */ if (index == 0) {
// This is the primary user.
- UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN
+ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL
} else {
// This isn't the primary user.
- 0
+ UserInfo.FLAG_FULL
},
UserManager.USER_TYPE_FULL_SYSTEM,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
index 0d8dd2c..df08efa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/AsyncSensorManagerTest.java
@@ -28,8 +28,8 @@
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.SensorManagerPlugin;
-import com.android.systemui.shared.plugins.PluginManager;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
import com.android.systemui.util.time.FakeSystemClock;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
index 2e74bf5..a0b4eab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeDialogImplTest.java
@@ -18,6 +18,7 @@
import static com.android.systemui.volume.VolumeDialogControllerImpl.STREAMS;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -28,6 +29,7 @@
import android.app.KeyguardManager;
import android.media.AudioManager;
import android.os.SystemClock;
+import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.InputDevice;
@@ -38,6 +40,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.Prefs;
import com.android.systemui.R;
@@ -49,6 +52,9 @@
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+import com.android.systemui.util.DeviceConfigProxyFake;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Before;
import org.junit.Test;
@@ -71,6 +77,8 @@
View mDrawerVibrate;
View mDrawerMute;
View mDrawerNormal;
+ private DeviceConfigProxyFake mDeviceConfigProxy;
+ private FakeExecutor mExecutor;
@Mock
VolumeDialogController mVolumeDialogController;
@@ -97,6 +105,9 @@
getContext().addMockSystemService(KeyguardManager.class, mKeyguard);
+ mDeviceConfigProxy = new DeviceConfigProxyFake();
+ mExecutor = new FakeExecutor(new FakeSystemClock());
+
mDialog = new VolumeDialogImpl(
getContext(),
mVolumeDialogController,
@@ -106,7 +117,9 @@
mMediaOutputDialogFactory,
mVolumePanelFactory,
mActivityStarter,
- mInteractionJankMonitor);
+ mInteractionJankMonitor,
+ mDeviceConfigProxy,
+ mExecutor);
mDialog.init(0, null);
State state = createShellState();
mDialog.onStateChangedH(state);
@@ -123,6 +136,9 @@
VolumePrefs.SHOW_RINGER_TOAST_COUNT + 1);
Prefs.putBoolean(mContext, Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, false);
+
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
}
private State createShellState() {
@@ -292,6 +308,35 @@
AudioManager.RINGER_MODE_NORMAL, false);
}
+ /**
+ * Ideally we would look at the ringer ImageView and check its assigned drawable id, but that
+ * API does not exist. So we do the next best thing; we check the cached icon id.
+ */
+ @Test
+ public void notificationVolumeSeparated_theRingerIconChanges() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "true", false);
+
+ mExecutor.runAllReady(); // for the config change to take effect
+
+ // assert icon is new based on res id
+ assertEquals(mDialog.mVolumeRingerIconDrawableId,
+ R.drawable.ic_speaker_on);
+ assertEquals(mDialog.mVolumeRingerMuteIconDrawableId,
+ R.drawable.ic_speaker_mute);
+ }
+
+ @Test
+ public void notificationVolumeNotSeparated_theRingerIconRemainsTheSame() {
+ mDeviceConfigProxy.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.VOLUME_SEPARATE_NOTIFICATION, "false", false);
+
+ mExecutor.runAllReady();
+
+ assertEquals(mDialog.mVolumeRingerIconDrawableId, R.drawable.ic_volume_ringer);
+ assertEquals(mDialog.mVolumeRingerMuteIconDrawableId, R.drawable.ic_volume_ringer_mute);
+ }
+
/*
@Test
public void testContentDescriptions() {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index a798f40..3601667 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -19,6 +19,7 @@
import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import kotlinx.coroutines.flow.Flow
@@ -53,6 +54,9 @@
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
override val statusBarState: Flow<StatusBarState> = _statusBarState
+ private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
+ override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
+
private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakePluginManager.java
index d245c72..63756c6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -18,7 +18,7 @@
import com.android.systemui.plugins.Plugin;
import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
public class FakePluginManager implements PluginManager {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
index dc6a8fb..ec1f352 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/LeakCheckedTest.java
@@ -18,7 +18,7 @@
import android.util.ArrayMap;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.plugins.PluginManager;
import com.android.systemui.statusbar.connectivity.NetworkController;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.StatusBarIconController;
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
index 5c92b34..06ca153 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/util/ScaleAwareTransitionProgressProvider.kt
@@ -14,7 +14,6 @@
*/
package com.android.systemui.unfold.util
-import android.animation.ValueAnimator
import android.content.ContentResolver
import android.database.ContentObserver
import android.provider.Settings
@@ -46,13 +45,15 @@
contentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
/* notifyForDescendants= */ false,
- animatorDurationScaleObserver)
+ animatorDurationScaleObserver
+ )
onAnimatorScaleChanged()
}
private fun onAnimatorScaleChanged() {
- val animationsEnabled = ValueAnimator.areAnimatorsEnabled()
- scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(animationsEnabled)
+ scopedUnfoldTransitionProgressProvider.setReadyToHandleTransition(
+ contentResolver.areAnimationsEnabled()
+ )
}
override fun addCallback(listener: TransitionProgressListener) {
@@ -74,4 +75,18 @@
progressProvider: UnfoldTransitionProgressProvider
): ScaleAwareTransitionProgressProvider
}
+
+ companion object {
+ fun ContentResolver.areAnimationsEnabled(): Boolean {
+ val animationScale =
+ Settings.Global.getStringForUser(
+ this,
+ Settings.Global.ANIMATOR_DURATION_SCALE,
+ this.userId
+ )
+ ?.toFloatOrNull()
+ ?: 1f
+ return animationScale != 0f
+ }
+ }
}
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 2f8dea7..e282679 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -1287,6 +1287,13 @@
}
@Override
+ public int getBatteryHealth() {
+ synchronized (mLock) {
+ return mHealthInfo.batteryHealth;
+ }
+ }
+
+ @Override
public boolean getBatteryLevelLow() {
synchronized (mLock) {
return mBatteryLevelLow;
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 912b1b2..c131ed6 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -523,8 +523,10 @@
* changed
*/
public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) {
- mSettingsObserver.setRefreshRates(displayDeviceConfig);
- mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig);
+ mSettingsObserver.setRefreshRates(displayDeviceConfig,
+ /* attemptLoadingFromDeviceConfig= */ true);
+ mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
+ /* attemptLoadingFromDeviceConfig= */ true);
}
/**
@@ -1142,19 +1144,25 @@
SettingsObserver(@NonNull Context context, @NonNull Handler handler) {
super(handler);
mContext = context;
- setRefreshRates(/* displayDeviceConfig= */ null);
+ // We don't want to load from the DeviceConfig while constructing since this leads to
+ // a spike in the latency of DisplayManagerService startup. This happens because
+ // reading from the DeviceConfig is an intensive IO operation and having it in the
+ // startup phase where we thrive to keep the latency very low has significant impact.
+ setRefreshRates(/* displayDeviceConfig= */ null,
+ /* attemptLoadingFromDeviceConfig= */ false);
}
/**
* This is used to update the refresh rate configs from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig) {
- setDefaultPeakRefreshRate(displayDeviceConfig);
+ public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
+ setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig);
mDefaultRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRate)
- : (float) displayDeviceConfig.getDefaultRefreshRate();
+ R.integer.config_defaultRefreshRate)
+ : (float) displayDeviceConfig.getDefaultRefreshRate();
}
public void observe() {
@@ -1215,13 +1223,27 @@
}
}
- private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig) {
+ @VisibleForTesting
+ float getDefaultRefreshRate() {
+ return mDefaultRefreshRate;
+ }
+
+ @VisibleForTesting
+ float getDefaultPeakRefreshRate() {
+ return mDefaultPeakRefreshRate;
+ }
+
+ private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
Float defaultPeakRefreshRate = null;
- try {
- defaultPeakRefreshRate =
+
+ if (attemptLoadingFromDeviceConfig) {
+ try {
+ defaultPeakRefreshRate =
mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
- } catch (Exception exception) {
- // Do nothing
+ } catch (Exception exception) {
+ // Do nothing
+ }
}
if (defaultPeakRefreshRate == null) {
defaultPeakRefreshRate =
@@ -1544,7 +1566,8 @@
mContext = context;
mHandler = handler;
mInjector = injector;
- updateBlockingZoneThresholds(/* displayDeviceConfig= */ null);
+ updateBlockingZoneThresholds(/* displayDeviceConfig= */ null,
+ /* attemptLoadingFromDeviceConfig= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
}
@@ -1553,22 +1576,44 @@
* This is used to update the blocking zone thresholds from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig) {
- loadLowBrightnessThresholds(displayDeviceConfig);
- loadHighBrightnessThresholds(displayDeviceConfig);
+ public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
+ loadLowBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ loadHighBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
}
- private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig) {
+ @VisibleForTesting
+ int[] getLowDisplayBrightnessThreshold() {
+ return mLowDisplayBrightnessThresholds;
+ }
+
+ @VisibleForTesting
+ int[] getLowAmbientBrightnessThreshold() {
+ return mLowAmbientBrightnessThresholds;
+ }
+
+ @VisibleForTesting
+ int[] getHighDisplayBrightnessThreshold() {
+ return mHighDisplayBrightnessThresholds;
+ }
+
+ @VisibleForTesting
+ int[] getHighAmbientBrightnessThreshold() {
+ return mHighAmbientBrightnessThresholds;
+ }
+
+ private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
@@ -1579,17 +1624,18 @@
}
}
- private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig) {
+ private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptLoadingFromDeviceConfig) {
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
() -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig);
+ displayDeviceConfig, attemptLoadingFromDeviceConfig);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
@@ -1605,13 +1651,16 @@
Callable<int[]> loadFromDeviceConfigDisplaySettingsCallable,
Callable<int[]> loadFromDisplayDeviceConfigCallable,
int brightnessThresholdOfFixedRefreshRateKey,
- DisplayDeviceConfig displayDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, boolean attemptLoadingFromDeviceConfig) {
int[] brightnessThresholds = null;
- try {
- brightnessThresholds =
+
+ if (attemptLoadingFromDeviceConfig) {
+ try {
+ brightnessThresholds =
loadFromDeviceConfigDisplaySettingsCallable.call();
- } catch (Exception exception) {
- // Do nothing
+ } catch (Exception exception) {
+ // Do nothing
+ }
}
if (brightnessThresholds == null) {
try {
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 8f6df0f..3a49d86 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -81,6 +81,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CopyOnWriteArrayList;
/**
* Service api for managing dreams.
@@ -120,6 +121,10 @@
private final boolean mDreamsEnabledByDefaultConfig;
private final boolean mDreamsActivatedOnChargeByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
+ private final boolean mKeepDreamingWhenUndockedDefault;
+
+ private final CopyOnWriteArrayList<DreamManagerInternal.DreamManagerStateListener>
+ mDreamManagerStateListeners = new CopyOnWriteArrayList<>();
@GuardedBy("mLock")
private DreamRecord mCurrentDream;
@@ -226,6 +231,8 @@
mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
mSettingsObserver = new SettingsObserver(mHandler);
+ mKeepDreamingWhenUndockedDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_keepDreamingWhenUndocking);
}
@Override
@@ -300,6 +307,7 @@
pw.println("mIsDocked=" + mIsDocked);
pw.println("mIsCharging=" + mIsCharging);
pw.println("mWhenToDream=" + mWhenToDream);
+ pw.println("mKeepDreamingWhenUndockedDefault=" + mKeepDreamingWhenUndockedDefault);
pw.println("getDozeComponent()=" + getDozeComponent());
pw.println();
@@ -328,7 +336,16 @@
}
}
- /** Whether a real dream is occurring. */
+ private void reportKeepDreamingWhenUndockedChanged(boolean keepDreaming) {
+ mHandler.post(() -> {
+ for (DreamManagerInternal.DreamManagerStateListener listener
+ : mDreamManagerStateListeners) {
+ listener.onKeepDreamingWhenUndockedChanged(keepDreaming);
+ }
+ });
+ }
+
+ /** Whether a real dream is occurring. */
private boolean isDreamingInternal() {
synchronized (mLock) {
return mCurrentDream != null && !mCurrentDream.isPreview
@@ -571,6 +588,8 @@
}
mSystemDreamComponent = componentName;
+ reportKeepDreamingWhenUndockedChanged(
+ mKeepDreamingWhenUndockedDefault && mSystemDreamComponent == null);
// Switch dream if currently dreaming and not dozing.
if (isDreamingInternal() && !isDozingInternal()) {
@@ -1012,6 +1031,22 @@
public void requestDream() {
requestDreamInternal();
}
+
+ @Override
+ public boolean keepDreamingWhenUndockedDefault() {
+ // This value does not change, so a lock should not be needed.
+ return mKeepDreamingWhenUndockedDefault;
+ }
+
+ @Override
+ public void registerDreamManagerStateListener(DreamManagerStateListener listener) {
+ mDreamManagerStateListeners.add(listener);
+ }
+
+ @Override
+ public void unregisterDreamManagerStateListener(DreamManagerStateListener listener) {
+ mDreamManagerStateListeners.remove(listener);
+ }
}
private static final class DreamRecord {
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index f867808..c9b3597 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -367,7 +367,7 @@
return;
}
- int newState = mOrderedStates[0].getIdentifier();
+ int newState = INVALID_DEVICE_STATE;
for (int i = 0; i < mOrderedStates.length; i++) {
int state = mOrderedStates[i].getIdentifier();
if (DEBUG) {
@@ -396,7 +396,7 @@
}
}
- if (newState != mLastReportedState) {
+ if (newState != INVALID_DEVICE_STATE && newState != mLastReportedState) {
mLastReportedState = newState;
stateToReport = newState;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 377a651..0d03133 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -419,6 +419,9 @@
// The current battery level percentage.
private int mBatteryLevel;
+ // The amount of battery drained while the device has been in a dream state.
+ private int mDreamsBatteryLevelDrain;
+
// True if updatePowerStateLocked() is already in progress.
// TODO(b/215518989): Remove this once transactions are in place
private boolean mUpdatePowerStateInProgress;
@@ -451,11 +454,6 @@
@GuardedBy("mEnhancedDischargeTimeLock")
private boolean mEnhancedDischargePredictionIsPersonalized;
- // The battery level percentage at the time the dream started.
- // This is used to terminate a dream and go to sleep if the battery is
- // draining faster than it is charging and the user activity timeout has expired.
- private int mBatteryLevelWhenDreamStarted;
-
// The current dock state.
private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
@@ -468,9 +466,6 @@
// True if the device should wake up when plugged or unplugged.
private boolean mWakeUpWhenPluggedOrUnpluggedConfig;
- // True if the device should keep dreaming when undocked.
- private boolean mKeepDreamingWhenUndockingConfig;
-
// True if the device should wake up when plugged or unplugged in theater mode.
private boolean mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig;
@@ -677,6 +672,19 @@
// but the DreamService has not yet been told to start (it's an async process).
private boolean mDozeStartInProgress;
+ // Whether to keep dreaming when the device is undocked.
+ private boolean mKeepDreamingWhenUndocked;
+
+ private final class DreamManagerStateListener implements
+ DreamManagerInternal.DreamManagerStateListener {
+ @Override
+ public void onKeepDreamingWhenUndockedChanged(boolean keepDreaming) {
+ synchronized (mLock) {
+ mKeepDreamingWhenUndocked = keepDreaming;
+ }
+ }
+ }
+
private final class PowerGroupWakefulnessChangeListener implements
PowerGroup.PowerGroupListener {
@GuardedBy("mLock")
@@ -1265,6 +1273,12 @@
new DisplayGroupPowerChangeListener();
mDisplayManagerInternal.registerDisplayGroupListener(displayGroupPowerChangeListener);
+ // These DreamManager methods do not acquire locks, so they should be safe to call.
+ mKeepDreamingWhenUndocked = mDreamManager.keepDreamingWhenUndockedDefault();
+ if (mKeepDreamingWhenUndocked) {
+ mDreamManager.registerDreamManagerStateListener(new DreamManagerStateListener());
+ }
+
mWirelessChargerDetector = mInjector.createWirelessChargerDetector(sensorManager,
mInjector.createSuspendBlocker(
this, "PowerManagerService.WirelessChargerDetector"),
@@ -1382,8 +1396,6 @@
com.android.internal.R.bool.config_powerDecoupleInteractiveModeFromDisplay);
mWakeUpWhenPluggedOrUnpluggedConfig = resources.getBoolean(
com.android.internal.R.bool.config_unplugTurnsOnScreen);
- mKeepDreamingWhenUndockingConfig = resources.getBoolean(
- com.android.internal.R.bool.config_keepDreamingWhenUndocking);
mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig = resources.getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromUnplug);
mSuspendWhenScreenOffDueToProximityConfig = resources.getBoolean(
@@ -2448,15 +2460,25 @@
final int oldPlugType = mPlugType;
mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
mPlugType = mBatteryManagerInternal.getPlugType();
+ final int oldBatteryLevel = mBatteryLevel;
mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
+ final boolean isOverheat = mBatteryManagerInternal.getBatteryHealth()
+ == BatteryManager.BATTERY_HEALTH_OVERHEAT;
if (DEBUG_SPEW) {
Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
+ ", mIsPowered=" + mIsPowered
+ ", oldPlugType=" + oldPlugType
+ ", mPlugType=" + mPlugType
- + ", mBatteryLevel=" + mBatteryLevel);
+ + ", oldBatteryLevel=" + oldBatteryLevel
+ + ", mBatteryLevel=" + mBatteryLevel
+ + ", isOverheat=" + isOverheat);
+ }
+
+ if (!isOverheat && oldBatteryLevel > 0
+ && getGlobalWakefulnessLocked() == WAKEFULNESS_DREAMING) {
+ mDreamsBatteryLevelDrain += (oldBatteryLevel - mBatteryLevel);
}
if (wasPowered != mIsPowered || oldPlugType != mPlugType) {
@@ -2508,7 +2530,7 @@
}
// Don't wake when undocking while dreaming if configured not to.
- if (mKeepDreamingWhenUndockingConfig
+ if (mKeepDreamingWhenUndocked
&& getGlobalWakefulnessLocked() == WAKEFULNESS_DREAMING
&& wasPowered && !mIsPowered
&& oldPlugType == BatteryManager.BATTERY_PLUGGED_DOCK) {
@@ -3278,7 +3300,7 @@
// Remember the initial battery level when the dream started.
if (startDreaming && isDreaming) {
- mBatteryLevelWhenDreamStarted = mBatteryLevel;
+ mDreamsBatteryLevelDrain = 0;
if (wakefulness == WAKEFULNESS_DOZING) {
Slog.i(TAG, "Dozing...");
} else {
@@ -3299,16 +3321,15 @@
if (wakefulness == WAKEFULNESS_DREAMING) {
if (isDreaming && canDreamLocked(powerGroup)) {
if (mDreamsBatteryLevelDrainCutoffConfig >= 0
- && mBatteryLevel < mBatteryLevelWhenDreamStarted
- - mDreamsBatteryLevelDrainCutoffConfig
+ && mDreamsBatteryLevelDrain > mDreamsBatteryLevelDrainCutoffConfig
&& !isBeingKeptAwakeLocked(powerGroup)) {
// If the user activity timeout expired and the battery appears
// to be draining faster than it is charging then stop dreaming
// and go to sleep.
Slog.i(TAG, "Stopping dream because the battery appears to "
+ "be draining faster than it is charging. "
- + "Battery level when dream started: "
- + mBatteryLevelWhenDreamStarted + "%. "
+ + "Battery level drained while dreaming: "
+ + mDreamsBatteryLevelDrain + "%. "
+ "Battery level now: " + mBatteryLevel + "%.");
} else {
return; // continue dreaming
@@ -3539,6 +3560,11 @@
mScreenBrightnessBoostInProgress);
}
+ @VisibleForTesting
+ int getDreamsBatteryLevelDrain() {
+ return mDreamsBatteryLevelDrain;
+ }
+
private final DisplayManagerInternal.DisplayPowerCallbacks mDisplayPowerCallbacks =
new DisplayManagerInternal.DisplayPowerCallbacks() {
@@ -4383,7 +4409,7 @@
pw.println(" mIsPowered=" + mIsPowered);
pw.println(" mPlugType=" + mPlugType);
pw.println(" mBatteryLevel=" + mBatteryLevel);
- pw.println(" mBatteryLevelWhenDreamStarted=" + mBatteryLevelWhenDreamStarted);
+ pw.println(" mDreamsBatteryLevelDrain=" + mDreamsBatteryLevelDrain);
pw.println(" mDockState=" + mDockState);
pw.println(" mStayOn=" + mStayOn);
pw.println(" mProximityPositive=" + mProximityPositive);
@@ -4457,8 +4483,7 @@
+ mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig);
pw.println(" mTheaterModeEnabled="
+ mTheaterModeEnabled);
- pw.println(" mKeepDreamingWhenUndockingConfig="
- + mKeepDreamingWhenUndockingConfig);
+ pw.println(" mKeepDreamingWhenUndocked=" + mKeepDreamingWhenUndocked);
pw.println(" mSuspendWhenScreenOffDueToProximityConfig="
+ mSuspendWhenScreenOffDueToProximityConfig);
pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig);
@@ -4625,8 +4650,8 @@
proto.write(PowerManagerServiceDumpProto.PLUG_TYPE, mPlugType);
proto.write(PowerManagerServiceDumpProto.BATTERY_LEVEL, mBatteryLevel);
proto.write(
- PowerManagerServiceDumpProto.BATTERY_LEVEL_WHEN_DREAM_STARTED,
- mBatteryLevelWhenDreamStarted);
+ PowerManagerServiceDumpProto.BATTERY_LEVEL_DRAINED_WHILE_DREAMING,
+ mDreamsBatteryLevelDrain);
proto.write(PowerManagerServiceDumpProto.DOCK_STATE, mDockState);
proto.write(PowerManagerServiceDumpProto.IS_STAY_ON, mStayOn);
proto.write(PowerManagerServiceDumpProto.IS_PROXIMITY_POSITIVE, mProximityPositive);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fc15890..66992aa 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5261,42 +5261,7 @@
}
// If we are preparing an app transition, then delay changing
// the visibility of this token until we execute that transition.
- // Note that we ignore display frozen since we want the opening / closing transition type
- // can be updated correctly even display frozen, and it's safe since in applyAnimation will
- // still check DC#okToAnimate again if the transition animation is fine to apply.
- // TODO(new-app-transition): Rewrite this logic using WM Shell.
- final boolean recentsAnimating = isAnimating(PARENTS, ANIMATION_TYPE_RECENTS);
- final boolean isEnteringPipWithoutVisibleChange = mWaitForEnteringPinnedMode
- && mVisible == visible;
- if (okToAnimate(true /* ignoreFrozen */, canTurnScreenOn())
- && (appTransition.isTransitionSet()
- || (recentsAnimating && !isActivityTypeHome()))
- // If the visibility is not changed during enter PIP, we don't want to include it in
- // app transition to affect the animation theme, because the Pip organizer will
- // animate the entering PIP instead.
- && !isEnteringPipWithoutVisibleChange) {
- if (visible) {
- displayContent.mOpeningApps.add(this);
- mEnteringAnimation = true;
- } else if (mVisible) {
- displayContent.mClosingApps.add(this);
- mEnteringAnimation = false;
- }
- if ((appTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0) {
- // We're launchingBehind, add the launching activity to mOpeningApps.
- final WindowState win = getDisplayContent().findFocusedWindow();
- if (win != null) {
- final ActivityRecord focusedActivity = win.mActivityRecord;
- if (focusedActivity != null) {
- ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
- "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps",
- focusedActivity);
-
- // Force animation to be loaded.
- displayContent.mOpeningApps.add(focusedActivity);
- }
- }
- }
+ if (deferCommitVisibilityChange(visible)) {
return;
}
@@ -5304,6 +5269,61 @@
updateReportedVisibilityLocked();
}
+ /**
+ * Returns {@code true} if this activity is either added to opening-apps or closing-apps.
+ * Then its visibility will be committed until the transition is ready.
+ */
+ private boolean deferCommitVisibilityChange(boolean visible) {
+ if (!mDisplayContent.mAppTransition.isTransitionSet()) {
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ // Shell transition doesn't use opening/closing sets.
+ return false;
+ }
+ // Defer committing visibility for non-home app which is animating by recents.
+ if (isActivityTypeHome() || !isAnimating(PARENTS, ANIMATION_TYPE_RECENTS)) {
+ return false;
+ }
+ }
+ if (mWaitForEnteringPinnedMode && mVisible == visible) {
+ // If the visibility is not changed during enter PIP, we don't want to include it in
+ // app transition to affect the animation theme, because the Pip organizer will
+ // animate the entering PIP instead.
+ return false;
+ }
+
+ // The animation will be visible soon so do not skip by screen off.
+ final boolean ignoreScreenOn = canTurnScreenOn() || mTaskSupervisor.getKeyguardController()
+ .isKeyguardGoingAway(mDisplayContent.mDisplayId);
+ // Ignore display frozen so the opening / closing transition type can be updated correctly
+ // even if the display is frozen. And it's safe since in applyAnimation will still check
+ // DC#okToAnimate again if the transition animation is fine to apply.
+ if (!okToAnimate(true /* ignoreFrozen */, ignoreScreenOn)) {
+ return false;
+ }
+ if (visible) {
+ mDisplayContent.mOpeningApps.add(this);
+ mEnteringAnimation = true;
+ } else if (mVisible) {
+ mDisplayContent.mClosingApps.add(this);
+ mEnteringAnimation = false;
+ }
+ if ((mDisplayContent.mAppTransition.getTransitFlags() & TRANSIT_FLAG_OPEN_BEHIND) != 0) {
+ // Add the launching-behind activity to mOpeningApps.
+ final WindowState win = mDisplayContent.findFocusedWindow();
+ if (win != null) {
+ final ActivityRecord focusedActivity = win.mActivityRecord;
+ if (focusedActivity != null) {
+ ProtoLog.d(WM_DEBUG_APP_TRANSITIONS,
+ "TRANSIT_FLAG_OPEN_BEHIND, adding %s to mOpeningApps",
+ focusedActivity);
+ // Force animation to be loaded.
+ mDisplayContent.mOpeningApps.add(focusedActivity);
+ }
+ }
+ }
+ return true;
+ }
+
@Override
boolean applyAnimation(LayoutParams lp, @TransitionOldType int transit, boolean enter,
boolean isVoiceInteraction, @Nullable ArrayList<WindowContainer> sources) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 54664f5..6eaeb15e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -2593,7 +2593,8 @@
// Apply options to prevent pendingOptions be taken when scheduling
// activity lifecycle transaction to make sure the override pending app
// transition will be applied immediately.
- if (activityOptions.getAnimationType() == ANIM_REMOTE_ANIMATION) {
+ if (activityOptions != null
+ && activityOptions.getAnimationType() == ANIM_REMOTE_ANIMATION) {
targetActivity.mPendingRemoteAnimation =
activityOptions.getRemoteAnimationAdapter();
}
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index 27370bf..adf862b 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -169,7 +169,8 @@
private final WindowManagerService mService;
private final DisplayContent mDisplayContent;
- private final TransitionAnimation mTransitionAnimation;
+ @VisibleForTesting
+ final TransitionAnimation mTransitionAnimation;
private @TransitionFlags int mNextAppTransitionFlags = 0;
private final ArrayList<Integer> mNextAppTransitionRequests = new ArrayList<>();
@@ -315,10 +316,33 @@
setAppTransitionState(APP_STATE_TIMEOUT);
}
+ /**
+ * Gets the animation overridden by app via {@link #overridePendingAppTransition}.
+ */
+ @Nullable
+ Animation getNextAppRequestedAnimation(boolean enter) {
+ final Animation a = mTransitionAnimation.loadAppTransitionAnimation(
+ mNextAppTransitionPackage,
+ enter ? mNextAppTransitionEnter : mNextAppTransitionExit);
+ if (mNextAppTransitionBackgroundColor != 0 && a != null) {
+ a.setBackdropColor(mNextAppTransitionBackgroundColor);
+ }
+ return a;
+ }
+
+ /**
+ * Gets the animation background color overridden by app via
+ * {@link #overridePendingAppTransition}.
+ */
@ColorInt int getNextAppTransitionBackgroundColor() {
return mNextAppTransitionBackgroundColor;
}
+ @VisibleForTesting
+ boolean isNextAppTransitionOverrideRequested() {
+ return mNextAppTransitionOverrideRequested;
+ }
+
HardwareBuffer getAppTransitionThumbnailHeader(WindowContainer container) {
AppTransitionAnimationSpec spec = mNextAppTransitionAnimationsSpecs.get(
container.hashCode());
@@ -411,9 +435,12 @@
}
void clear() {
+ clear(true /* clearAppOverride */);
+ }
+
+ private void clear(boolean clearAppOverride) {
mNextAppTransitionType = NEXT_TRANSIT_TYPE_NONE;
mNextAppTransitionOverrideRequested = false;
- mNextAppTransitionPackage = null;
mNextAppTransitionAnimationsSpecs.clear();
mRemoteAnimationController = null;
mNextAppTransitionAnimationsSpecsFuture = null;
@@ -421,6 +448,12 @@
mAnimationFinishedCallback = null;
mOverrideTaskTransition = false;
mNextAppTransitionIsSync = false;
+ if (clearAppOverride) {
+ mNextAppTransitionPackage = null;
+ mNextAppTransitionEnter = 0;
+ mNextAppTransitionExit = 0;
+ mNextAppTransitionBackgroundColor = 0;
+ }
}
void freeze() {
@@ -528,7 +561,7 @@
return TransitionAnimation.loadAnimationSafely(context, resId, TAG);
}
- static int mapOpenCloseTransitTypes(int transit, boolean enter) {
+ private static int mapOpenCloseTransitTypes(int transit, boolean enter) {
int animAttr = 0;
switch (transit) {
case TRANSIT_OLD_ACTIVITY_OPEN:
@@ -788,11 +821,7 @@
"applyAnimation: anim=%s transit=%s Callers=%s", a,
appTransitionOldToString(transit), Debug.getCallers(3));
} else if (mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM) {
- a = mTransitionAnimation.loadAppTransitionAnimation(mNextAppTransitionPackage,
- enter ? mNextAppTransitionEnter : mNextAppTransitionExit);
- if (mNextAppTransitionBackgroundColor != 0) {
- a.setBackdropColor(mNextAppTransitionBackgroundColor);
- }
+ a = getNextAppRequestedAnimation(enter);
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM,
"applyAnimation: anim=%s nextAppTransition=ANIM_CUSTOM transit=%s "
+ "isEntrance=%b Callers=%s",
@@ -1032,7 +1061,9 @@
ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s",
isTransitionSet(), remoteAnimationAdapter);
if (isTransitionSet() && !mNextAppTransitionIsSync) {
- clear();
+ // ActivityEmbedding animation will run by the app process for which we want to respect
+ // the app override for whether or not to show background color.
+ clear(!isActivityEmbedding /* clearAppOverride */);
mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent,
remoteAnimationAdapter, mHandler, isActivityEmbedding);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 12133bc..d5c9e66 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -562,6 +562,34 @@
}
/**
+ * Whether the transition contains any embedded {@link TaskFragment} that does not fill the
+ * parent {@link Task} before or after the transition.
+ */
+ private boolean transitionContainsTaskFragmentWithBoundsOverride() {
+ for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
+ final WindowContainer wc = mDisplayContent.mChangingContainers.valueAt(i);
+ if (wc.isEmbedded()) {
+ // Contains embedded TaskFragment with bounds changed.
+ return true;
+ }
+ }
+ mTempTransitionWindows.clear();
+ mTempTransitionWindows.addAll(mDisplayContent.mClosingApps);
+ mTempTransitionWindows.addAll(mDisplayContent.mOpeningApps);
+ boolean containsTaskFragmentWithBoundsOverride = false;
+ for (int i = mTempTransitionWindows.size() - 1; i >= 0; i--) {
+ final ActivityRecord r = mTempTransitionWindows.get(i).asActivityRecord();
+ final TaskFragment tf = r.getTaskFragment();
+ if (tf != null && tf.isEmbeddedWithBoundsOverride()) {
+ containsTaskFragmentWithBoundsOverride = true;
+ break;
+ }
+ }
+ mTempTransitionWindows.clear();
+ return containsTaskFragmentWithBoundsOverride;
+ }
+
+ /**
* Finds the common parent {@link Task} that is parent of all embedded app windows in the
* current transition.
* @return {@code null} if app windows in the transition are not children of the same Task, or
@@ -664,12 +692,17 @@
if (transitionMayContainNonAppWindows(transit)) {
return false;
}
+ if (!transitionContainsTaskFragmentWithBoundsOverride()) {
+ // No need to play TaskFragment remote animation if all embedded TaskFragment in the
+ // transition fill the Task.
+ return false;
+ }
final Task task = findParentTaskForAllEmbeddedWindows();
final ITaskFragmentOrganizer organizer = findTaskFragmentOrganizer(task);
final RemoteAnimationDefinition definition = organizer != null
? mDisplayContent.mAtmService.mTaskFragmentOrganizerController
- .getRemoteAnimationDefinition(organizer, task.mTaskId)
+ .getRemoteAnimationDefinition(organizer)
: null;
final RemoteAnimationAdapter adapter = definition != null
? definition.getAdapter(transit, activityTypes)
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 38f6a53..3a936a5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4935,9 +4935,8 @@
@Override
boolean okToAnimate(boolean ignoreFrozen, boolean ignoreScreenOn) {
return okToDisplay(ignoreFrozen, ignoreScreenOn)
- && (mDisplayId != DEFAULT_DISPLAY
- || mWmService.mPolicy.okToAnimate(ignoreScreenOn))
- && getDisplayPolicy().isScreenOnFully();
+ && (mDisplayId != DEFAULT_DISPLAY || mWmService.mPolicy.okToAnimate(ignoreScreenOn))
+ && (ignoreFrozen || mDisplayPolicy.isScreenOnFully());
}
static final class TaskForResizePointSearchResult implements Predicate<Task> {
diff --git a/services/core/java/com/android/server/wm/LaunchParamsUtil.java b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
index 4122992..09a17e1 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsUtil.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsUtil.java
@@ -26,6 +26,7 @@
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
import android.util.Size;
+import android.view.View;
/**
* The static class that defines some utility constants and functions that are shared among launch
@@ -43,6 +44,10 @@
private static final int DEFAULT_LANDSCAPE_FREEFORM_WIDTH_DP = 1064;
private static final int DEFAULT_LANDSCAPE_FREEFORM_HEIGHT_DP = 600;
+ private static final int DISPLAY_EDGE_OFFSET_DP = 27;
+
+ private static final Rect TMP_STABLE_BOUNDS = new Rect();
+
private LaunchParamsUtil() {}
/**
@@ -126,4 +131,68 @@
return new Size(adjWidth, adjHeight);
}
+
+ static void adjustBoundsToFitInDisplayArea(@NonNull TaskDisplayArea displayArea,
+ int layoutDirection,
+ @NonNull ActivityInfo.WindowLayout layout,
+ @NonNull Rect inOutBounds) {
+ // Give a small margin between the window bounds and the display bounds.
+ final Rect stableBounds = TMP_STABLE_BOUNDS;
+ displayArea.getStableRect(stableBounds);
+ final float density = (float) displayArea.getConfiguration().densityDpi / DENSITY_DEFAULT;
+ final int displayEdgeOffset = (int) (DISPLAY_EDGE_OFFSET_DP * density + 0.5f);
+ stableBounds.inset(displayEdgeOffset, displayEdgeOffset);
+
+ if (stableBounds.width() < inOutBounds.width()
+ || stableBounds.height() < inOutBounds.height()) {
+ final float heightShrinkRatio = stableBounds.width() / (float) inOutBounds.width();
+ final float widthShrinkRatio =
+ stableBounds.height() / (float) inOutBounds.height();
+ final float shrinkRatio = Math.min(heightShrinkRatio, widthShrinkRatio);
+ // Minimum layout requirements.
+ final int layoutMinWidth = (layout == null) ? -1 : layout.minWidth;
+ final int layoutMinHeight = (layout == null) ? -1 : layout.minHeight;
+ int adjustedWidth = Math.max(layoutMinWidth, (int) (inOutBounds.width() * shrinkRatio));
+ int adjustedHeight = Math.max(layoutMinHeight,
+ (int) (inOutBounds.height() * shrinkRatio));
+ if (stableBounds.width() < adjustedWidth
+ || stableBounds.height() < adjustedHeight) {
+ // There is no way for us to fit the bounds in the displayArea without breaking min
+ // size constraints. Set the min size to make visible as much content as possible.
+ final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
+ ? stableBounds.right - adjustedWidth
+ : stableBounds.left;
+ inOutBounds.set(left, stableBounds.top, left + adjustedWidth,
+ stableBounds.top + adjustedHeight);
+ return;
+ }
+ inOutBounds.set(inOutBounds.left, inOutBounds.top,
+ inOutBounds.left + adjustedWidth, inOutBounds.top + adjustedHeight);
+ }
+
+ final int dx;
+ if (inOutBounds.right > stableBounds.right) {
+ // Right edge is out of displayArea.
+ dx = stableBounds.right - inOutBounds.right;
+ } else if (inOutBounds.left < stableBounds.left) {
+ // Left edge is out of displayArea.
+ dx = stableBounds.left - inOutBounds.left;
+ } else {
+ // Vertical edges are all in displayArea.
+ dx = 0;
+ }
+
+ final int dy;
+ if (inOutBounds.top < stableBounds.top) {
+ // Top edge is out of displayArea.
+ dy = stableBounds.top - inOutBounds.top;
+ } else if (inOutBounds.bottom > stableBounds.bottom) {
+ // Bottom edge is out of displayArea.
+ dy = stableBounds.bottom - inOutBounds.bottom;
+ } else {
+ // Horizontal edges are all in displayArea.
+ dy = 0;
+ }
+ inOutBounds.offset(dx, dy);
+ }
}
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index ff09163..0cadc25 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -929,6 +929,7 @@
// Update windowing mode if necessary, e.g. launch into a different windowing mode.
if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
&& candidateTask.getWindowingMode() != windowingMode) {
+ candidateTask.mTransitionController.collect(candidateTask);
candidateTask.setWindowingMode(windowingMode);
}
return candidateTask.getRootTask();
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 52df3ca..273b4c4 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -581,15 +581,7 @@
@Override
boolean isEmbedded() {
- if (mIsEmbedded) {
- return true;
- }
- final WindowContainer<?> parent = getParent();
- if (parent != null) {
- final TaskFragment taskFragment = parent.asTaskFragment();
- return taskFragment != null && taskFragment.isEmbedded();
- }
- return false;
+ return mIsEmbedded;
}
@EmbeddingCheckResult
@@ -2519,6 +2511,22 @@
return mTaskFragmentOrganizer != null;
}
+ /**
+ * Whether this is an embedded {@link TaskFragment} that does not fill the parent {@link Task}.
+ */
+ boolean isEmbeddedWithBoundsOverride() {
+ if (!mIsEmbedded) {
+ return false;
+ }
+ final Task task = getTask();
+ if (task == null) {
+ return false;
+ }
+ final Rect taskBounds = task.getBounds();
+ final Rect taskFragBounds = getBounds();
+ return !taskBounds.equals(taskFragBounds) && taskBounds.contains(taskFragBounds);
+ }
+
/** Whether the Task should be visible. */
boolean isTaskVisibleRequested() {
final Task task = getTask();
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 2e716ae..6e4df79 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -36,7 +36,6 @@
import android.annotation.Nullable;
import android.content.Intent;
import android.content.res.Configuration;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -133,12 +132,11 @@
new WeakHashMap<>();
/**
- * Map from Task Id to {@link RemoteAnimationDefinition}.
- * @see android.window.TaskFragmentOrganizer#registerRemoteAnimations(int,
- * RemoteAnimationDefinition) )
+ * {@link RemoteAnimationDefinition} for embedded activities transition animation that is
+ * organized by this organizer.
*/
- private final SparseArray<RemoteAnimationDefinition> mRemoteAnimationDefinitions =
- new SparseArray<>();
+ @Nullable
+ private RemoteAnimationDefinition mRemoteAnimationDefinition;
/**
* Map from {@link TaskFragmentTransaction#getTransactionToken()} to the
@@ -427,7 +425,7 @@
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"Register task fragment organizer=%s uid=%d pid=%d",
organizer.asBinder(), uid, pid);
- if (mTaskFragmentOrganizerState.containsKey(organizer.asBinder())) {
+ if (isOrganizerRegistered(organizer)) {
throw new IllegalStateException(
"Replacing existing organizer currently unsupported");
}
@@ -455,7 +453,7 @@
}
@Override
- public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId,
+ public void registerRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer,
@NonNull RemoteAnimationDefinition definition) {
final int pid = Binder.getCallingPid();
final int uid = Binder.getCallingUid();
@@ -468,20 +466,19 @@
if (organizerState == null) {
throw new IllegalStateException("The organizer hasn't been registered.");
}
- if (organizerState.mRemoteAnimationDefinitions.contains(taskId)) {
+ if (organizerState.mRemoteAnimationDefinition != null) {
throw new IllegalStateException(
"The organizer has already registered remote animations="
- + organizerState.mRemoteAnimationDefinitions.get(taskId)
- + " for TaskId=" + taskId);
+ + organizerState.mRemoteAnimationDefinition);
}
definition.setCallingPidUid(pid, uid);
- organizerState.mRemoteAnimationDefinitions.put(taskId, definition);
+ organizerState.mRemoteAnimationDefinition = definition;
}
}
@Override
- public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer, int taskId) {
+ public void unregisterRemoteAnimations(@NonNull ITaskFragmentOrganizer organizer) {
final int pid = Binder.getCallingPid();
final long uid = Binder.getCallingUid();
synchronized (mGlobalLock) {
@@ -495,7 +492,7 @@
return;
}
- organizerState.mRemoteAnimationDefinitions.remove(taskId);
+ organizerState.mRemoteAnimationDefinition = null;
}
}
@@ -505,10 +502,18 @@
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
// Keep the calling identity to avoid unsecure change.
synchronized (mGlobalLock) {
- applyTransaction(wct, transitionType, shouldApplyIndependently);
- final TaskFragmentOrganizerState state = validateAndGetState(
- wct.getTaskFragmentOrganizer());
- state.onTransactionFinished(transactionToken);
+ if (isValidTransaction(wct)) {
+ applyTransaction(wct, transitionType, shouldApplyIndependently);
+ }
+ // Even if the transaction is empty, we still need to invoke #onTransactionFinished
+ // unless the organizer has been unregistered.
+ final ITaskFragmentOrganizer organizer = wct.getTaskFragmentOrganizer();
+ final TaskFragmentOrganizerState state = organizer != null
+ ? mTaskFragmentOrganizerState.get(organizer.asBinder())
+ : null;
+ if (state != null) {
+ state.onTransactionFinished(transactionToken);
+ }
}
}
@@ -517,7 +522,7 @@
@WindowManager.TransitionType int transitionType, boolean shouldApplyIndependently) {
// Keep the calling identity to avoid unsecure change.
synchronized (mGlobalLock) {
- if (wct.isEmpty()) {
+ if (!isValidTransaction(wct)) {
return;
}
mWindowOrganizerController.applyTaskFragmentTransactionLocked(wct, transitionType,
@@ -527,16 +532,16 @@
/**
* Gets the {@link RemoteAnimationDefinition} set on the given organizer if exists. Returns
- * {@code null} if it doesn't, or if the organizer has activity(ies) embedded in untrusted mode.
+ * {@code null} if it doesn't.
*/
@Nullable
public RemoteAnimationDefinition getRemoteAnimationDefinition(
- @NonNull ITaskFragmentOrganizer organizer, int taskId) {
+ @NonNull ITaskFragmentOrganizer organizer) {
synchronized (mGlobalLock) {
final TaskFragmentOrganizerState organizerState =
mTaskFragmentOrganizerState.get(organizer.asBinder());
return organizerState != null
- ? organizerState.mRemoteAnimationDefinitions.get(taskId)
+ ? organizerState.mRemoteAnimationDefinition
: null;
}
}
@@ -658,7 +663,7 @@
}
organizer = organizedTf[0].getTaskFragmentOrganizer();
}
- if (!mTaskFragmentOrganizerState.containsKey(organizer.asBinder())) {
+ if (!isOrganizerRegistered(organizer)) {
Slog.w(TAG, "The last TaskFragmentOrganizer no longer exists");
return;
}
@@ -704,7 +709,7 @@
mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).remove(event);
}
- boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
+ private boolean isOrganizerRegistered(@NonNull ITaskFragmentOrganizer organizer) {
return mTaskFragmentOrganizerState.containsKey(organizer.asBinder());
}
@@ -741,6 +746,20 @@
return state;
}
+ boolean isValidTransaction(@NonNull WindowContainerTransaction t) {
+ if (t.isEmpty()) {
+ return false;
+ }
+ final ITaskFragmentOrganizer organizer = t.getTaskFragmentOrganizer();
+ if (t.getTaskFragmentOrganizer() == null || !isOrganizerRegistered(organizer)) {
+ // Transaction from an unregistered organizer should not be applied. This can happen
+ // when the organizer process died before the transaction is applied.
+ Slog.e(TAG, "Caller organizer=" + organizer + " is no longer registered");
+ return false;
+ }
+ return true;
+ }
+
/**
* A class to store {@link ITaskFragmentOrganizer} and its organized
* {@link TaskFragment TaskFragments} with different pending event request.
@@ -1085,16 +1104,7 @@
return false;
}
final TaskFragment taskFragment = activity.getOrganizedTaskFragment();
- if (taskFragment == null) {
- return false;
- }
- final Task parentTask = taskFragment.getTask();
- if (parentTask != null) {
- final Rect taskBounds = parentTask.getBounds();
- final Rect taskFragBounds = taskFragment.getBounds();
- return !taskBounds.equals(taskFragBounds) && taskBounds.contains(taskFragBounds);
- }
- return false;
+ return taskFragment != null && taskFragment.isEmbeddedWithBoundsOverride();
}
}
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 4edda74..14a2d03 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -50,7 +50,6 @@
import android.util.Size;
import android.util.Slog;
import android.view.Gravity;
-import android.view.View;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -181,26 +180,34 @@
// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
// different, we should recalculating the bounds.
boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = false;
- final boolean canApplyFreeformPolicy =
+ // Note that initial bounds needs to be set to fullscreen tasks too as it's used as restore
+ // bounds.
+ final boolean canCalculateBoundsForFullscreenTask =
+ canCalculateBoundsForFullscreenTask(suggestedDisplayArea, launchMode);
+ final boolean canApplyFreeformWindowPolicy =
canApplyFreeformWindowPolicy(suggestedDisplayArea, launchMode);
- if (mSupervisor.canUseActivityOptionsLaunchBounds(options)
- && (canApplyFreeformPolicy || canApplyPipWindowPolicy(launchMode))) {
+ final boolean canApplyWindowLayout = layout != null
+ && (canApplyFreeformWindowPolicy || canCalculateBoundsForFullscreenTask);
+ final boolean canApplyBoundsFromActivityOptions =
+ mSupervisor.canUseActivityOptionsLaunchBounds(options)
+ && (canApplyFreeformWindowPolicy
+ || canApplyPipWindowPolicy(launchMode)
+ || canCalculateBoundsForFullscreenTask);
+
+ if (canApplyBoundsFromActivityOptions) {
hasInitialBounds = true;
- launchMode = launchMode == WINDOWING_MODE_UNDEFINED
+ // |launchMode| at this point can be fullscreen, PIP, MultiWindow, etc. Only set
+ // freeform windowing mode if appropriate by checking |canApplyFreeformWindowPolicy|.
+ launchMode = launchMode == WINDOWING_MODE_UNDEFINED && canApplyFreeformWindowPolicy
? WINDOWING_MODE_FREEFORM
: launchMode;
outParams.mBounds.set(options.getLaunchBounds());
if (DEBUG) appendLog("activity-options-bounds=" + outParams.mBounds);
- } else if (launchMode == WINDOWING_MODE_PINNED) {
- // System controls PIP window's bounds, so don't apply launch bounds.
- if (DEBUG) appendLog("empty-window-layout-for-pip");
- } else if (launchMode == WINDOWING_MODE_FULLSCREEN) {
- if (DEBUG) appendLog("activity-options-fullscreen=" + outParams.mBounds);
- } else if (layout != null && canApplyFreeformPolicy) {
+ } else if (canApplyWindowLayout) {
mTmpBounds.set(currentParams.mBounds);
getLayoutBounds(suggestedDisplayArea, root, layout, mTmpBounds);
if (!mTmpBounds.isEmpty()) {
- launchMode = WINDOWING_MODE_FREEFORM;
+ launchMode = canApplyFreeformWindowPolicy ? WINDOWING_MODE_FREEFORM : launchMode;
outParams.mBounds.set(mTmpBounds);
hasInitialBounds = true;
hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = true;
@@ -210,6 +217,8 @@
}
} else if (launchMode == WINDOWING_MODE_MULTI_WINDOW
&& options != null && options.getLaunchBounds() != null) {
+ // TODO: Investigate whether we can migrate this clause to the
+ // |canApplyBoundsFromActivityOptions| case above.
outParams.mBounds.set(options.getLaunchBounds());
hasInitialBounds = true;
if (DEBUG) appendLog("multiwindow-activity-options-bounds=" + outParams.mBounds);
@@ -249,11 +258,9 @@
if (!currentParams.mBounds.isEmpty()) {
// Carry over bounds from callers regardless of launch mode because bounds is still
// used to restore last non-fullscreen bounds when launch mode is not freeform.
- // Therefore it's not a resolution step for non-freeform launch mode and only
- // consider it fully resolved only when launch mode is freeform.
outParams.mBounds.set(currentParams.mBounds);
+ fullyResolvedCurrentParam = true;
if (launchMode == WINDOWING_MODE_FREEFORM) {
- fullyResolvedCurrentParam = true;
if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
}
}
@@ -362,13 +369,13 @@
if (resolvedMode == WINDOWING_MODE_FREEFORM) {
// Make sure bounds are in the displayArea.
if (currentParams.mPreferredTaskDisplayArea != taskDisplayArea) {
- adjustBoundsToFitInDisplayArea(taskDisplayArea, outParams.mBounds);
+ adjustBoundsToFitInDisplayArea(taskDisplayArea, layout, outParams.mBounds);
}
// Even though we want to keep original bounds, we still don't want it to stomp on
// an existing task.
adjustBoundsToAvoidConflictInDisplayArea(taskDisplayArea, outParams.mBounds);
}
- } else if (taskDisplayArea.inFreeformWindowingMode()) {
+ } else {
if (source != null && source.inFreeformWindowingMode()
&& resolvedMode == WINDOWING_MODE_FREEFORM
&& outParams.mBounds.isEmpty()
@@ -545,10 +552,19 @@
return display.getDisplayId() == source.getDisplayId();
}
+ private boolean canCalculateBoundsForFullscreenTask(@NonNull TaskDisplayArea displayArea,
+ int launchMode) {
+ return mSupervisor.mService.mSupportsFreeformWindowManagement
+ && ((displayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && launchMode == WINDOWING_MODE_UNDEFINED)
+ || launchMode == WINDOWING_MODE_FULLSCREEN);
+ }
+
private boolean canApplyFreeformWindowPolicy(@NonNull TaskDisplayArea suggestedDisplayArea,
int launchMode) {
return mSupervisor.mService.mSupportsFreeformWindowManagement
- && (suggestedDisplayArea.inFreeformWindowingMode()
+ && ((suggestedDisplayArea.inFreeformWindowingMode()
+ && launchMode == WINDOWING_MODE_UNDEFINED)
|| launchMode == WINDOWING_MODE_FREEFORM);
}
@@ -710,16 +726,10 @@
private void getTaskBounds(@NonNull ActivityRecord root, @NonNull TaskDisplayArea displayArea,
@NonNull ActivityInfo.WindowLayout layout, int resolvedMode, boolean hasInitialBounds,
@NonNull Rect inOutBounds) {
- if (resolvedMode == WINDOWING_MODE_FULLSCREEN) {
- // We don't handle letterboxing here. Letterboxing will be handled by valid checks
- // later.
- inOutBounds.setEmpty();
- if (DEBUG) appendLog("maximized-bounds");
- return;
- }
-
- if (resolvedMode != WINDOWING_MODE_FREEFORM) {
- // We don't apply freeform bounds adjustment to other windowing modes.
+ if (resolvedMode != WINDOWING_MODE_FREEFORM
+ && resolvedMode != WINDOWING_MODE_FULLSCREEN) {
+ // This function should be used only for freeform bounds adjustment. Freeform bounds
+ // needs to be set to fullscreen tasks too as restore bounds.
if (DEBUG) {
appendLog("skip-bounds-" + WindowConfiguration.windowingModeToString(resolvedMode));
}
@@ -758,9 +768,10 @@
// to the center of suggested bounds (or the displayArea if no suggested bounds). The
// default size might be too big to center to source activity bounds in displayArea, so
// we may need to move it back to the displayArea.
+ adjustBoundsToFitInDisplayArea(displayArea, layout, mTmpBounds);
+ inOutBounds.setEmpty();
LaunchParamsUtil.centerBounds(displayArea, mTmpBounds.width(), mTmpBounds.height(),
inOutBounds);
- adjustBoundsToFitInDisplayArea(displayArea, inOutBounds);
if (DEBUG) appendLog("freeform-size-mismatch=" + inOutBounds);
}
@@ -807,47 +818,12 @@
}
private void adjustBoundsToFitInDisplayArea(@NonNull TaskDisplayArea displayArea,
- @NonNull Rect inOutBounds) {
- final Rect stableBounds = mTmpStableBounds;
- displayArea.getStableRect(stableBounds);
-
- if (stableBounds.width() < inOutBounds.width()
- || stableBounds.height() < inOutBounds.height()) {
- // There is no way for us to fit the bounds in the displayArea without changing width
- // or height. Just move the start to align with the displayArea.
- final int layoutDirection =
- mSupervisor.mRootWindowContainer.getConfiguration().getLayoutDirection();
- final int left = layoutDirection == View.LAYOUT_DIRECTION_RTL
- ? stableBounds.right - inOutBounds.right + inOutBounds.left
- : stableBounds.left;
- inOutBounds.offsetTo(left, stableBounds.top);
- return;
- }
-
- final int dx;
- if (inOutBounds.right > stableBounds.right) {
- // Right edge is out of displayArea.
- dx = stableBounds.right - inOutBounds.right;
- } else if (inOutBounds.left < stableBounds.left) {
- // Left edge is out of displayArea.
- dx = stableBounds.left - inOutBounds.left;
- } else {
- // Vertical edges are all in displayArea.
- dx = 0;
- }
-
- final int dy;
- if (inOutBounds.top < stableBounds.top) {
- // Top edge is out of displayArea.
- dy = stableBounds.top - inOutBounds.top;
- } else if (inOutBounds.bottom > stableBounds.bottom) {
- // Bottom edge is out of displayArea.
- dy = stableBounds.bottom - inOutBounds.bottom;
- } else {
- // Horizontal edges are all in displayArea.
- dy = 0;
- }
- inOutBounds.offset(dx, dy);
+ @NonNull ActivityInfo.WindowLayout layout,
+ @NonNull Rect inOutBounds) {
+ final int layoutDirection = mSupervisor.mRootWindowContainer.getConfiguration()
+ .getLayoutDirection();
+ LaunchParamsUtil.adjustBoundsToFitInDisplayArea(displayArea, layoutDirection, layout,
+ inOutBounds);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index fa1bc54..80357eb 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2995,10 +2995,9 @@
// screen empty. Show background color to cover that.
showBackdrop = getDisplayContent().mChangingContainers.size() > 1;
} else {
- // Check whether or not to show backdrop for open/close transition.
- final int animAttr = AppTransition.mapOpenCloseTransitTypes(transit, enter);
- final Animation a = animAttr != 0
- ? appTransition.loadAnimationAttr(lp, animAttr, transit) : null;
+ // Check whether the app has requested to show backdrop for open/close
+ // transition.
+ final Animation a = appTransition.getNextAppRequestedAnimation(enter);
showBackdrop = a != null && a.getShowBackdrop();
}
backdropColor = appTransition.getNextAppTransitionBackgroundColor();
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 557c0ef..b30fd07 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -403,9 +403,6 @@
*/
void applyTaskFragmentTransactionLocked(@NonNull WindowContainerTransaction wct,
@WindowManager.TransitionType int type, boolean shouldApplyIndependently) {
- if (!isValidTransaction(wct)) {
- return;
- }
enforceTaskFragmentOrganizerPermission("applyTaskFragmentTransaction()",
Objects.requireNonNull(wct.getTaskFragmentOrganizer()),
Objects.requireNonNull(wct));
@@ -459,7 +456,7 @@
// calls startSyncSet.
() -> mTransitionController.moveToCollecting(nextTransition),
() -> {
- if (isValidTransaction(wct)) {
+ if (mTaskFragmentOrganizerController.isValidTransaction(wct)) {
applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
mTransitionController.requestStartTransition(nextTransition,
null /* startTask */, null /* remoteTransition */,
@@ -1620,18 +1617,6 @@
return (cfgChanges & CONTROLLABLE_CONFIGS) == 0;
}
- private boolean isValidTransaction(@NonNull WindowContainerTransaction t) {
- if (t.getTaskFragmentOrganizer() != null && !mTaskFragmentOrganizerController
- .isOrganizerRegistered(t.getTaskFragmentOrganizer())) {
- // Transaction from an unregistered organizer should not be applied. This can happen
- // when the organizer process died before the transaction is applied.
- Slog.e(TAG, "Caller organizer=" + t.getTaskFragmentOrganizer()
- + " is no longer registered");
- return false;
- }
- return true;
- }
-
/**
* Makes sure that the transaction only contains operations that are allowed for the
* {@link WindowContainerTransaction#getTaskFragmentOrganizer()}.
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 18dd264..fb0cdfa 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -21,6 +21,7 @@
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS;
+import static android.hardware.display.DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT;
import static android.hardware.display.DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE;
@@ -31,6 +32,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -48,6 +51,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
+import android.content.res.Resources;
import android.database.ContentObserver;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
@@ -76,6 +80,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.internal.util.Preconditions;
import com.android.internal.util.test.FakeSettingsProvider;
@@ -1855,16 +1860,83 @@
@Test
public void testNotifyDefaultDisplayDeviceUpdated() {
- DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
- when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{});
- when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{});
- when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{});
- when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{});
+ Resources resources = mock(Resources.class);
+ when(mContext.getResources()).thenReturn(resources);
+ when(resources.getInteger(com.android.internal.R.integer.config_defaultPeakRefreshRate))
+ .thenReturn(75);
+ when(resources.getInteger(R.integer.config_defaultRefreshRate))
+ .thenReturn(45);
+ when(resources.getIntArray(R.array.config_brightnessThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{5});
+ when(resources.getIntArray(R.array.config_ambientThresholdsOfPeakRefreshRate))
+ .thenReturn(new int[]{10});
+ when(
+ resources.getIntArray(R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{250});
+ when(
+ resources.getIntArray(R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate))
+ .thenReturn(new int[]{7000});
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[]{60.0f, 90.0f}, 0);
+ // We don't expect any interaction with DeviceConfig when the director is initialized
+ // because we explicitly avoid doing this as this can lead to a latency spike in the
+ // startup of DisplayManagerService
+ // Verify all the loaded values are from DisplayDeviceConfig
+ assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 45, 0.0);
+ assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 75,
+ 0.0);
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
+ new int[]{250});
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
+ new int[]{7000});
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
+ new int[]{5});
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
+ new int[]{10});
+
+ // Notify that the default display is updated, such that DisplayDeviceConfig has new values
+ DisplayDeviceConfig displayDeviceConfig = mock(DisplayDeviceConfig.class);
+ when(displayDeviceConfig.getDefaultRefreshRate()).thenReturn(50);
+ when(displayDeviceConfig.getDefaultPeakRefreshRate()).thenReturn(55);
+ when(displayDeviceConfig.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
+ when(displayDeviceConfig.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
+ when(displayDeviceConfig.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
+ when(displayDeviceConfig.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
director.defaultDisplayDeviceUpdated(displayDeviceConfig);
- verify(displayDeviceConfig).getDefaultRefreshRate();
- verify(displayDeviceConfig).getDefaultPeakRefreshRate();
+
+ assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0);
+ assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 55,
+ 0.0);
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
+ new int[]{210});
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
+ new int[]{2100});
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
+ new int[]{25});
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
+ new int[]{30});
+
+ // Notify that the default display is updated, such that DeviceConfig has new values
+ FakeDeviceConfig config = mInjector.getDeviceConfig();
+ config.setDefaultPeakRefreshRate(60);
+ config.setLowAmbientBrightnessThresholds(new int[]{20});
+ config.setLowDisplayBrightnessThresholds(new int[]{10});
+ config.setHighDisplayBrightnessThresholds(new int[]{255});
+ config.setHighAmbientBrightnessThresholds(new int[]{8000});
+
+ director.defaultDisplayDeviceUpdated(displayDeviceConfig);
+
+ assertEquals(director.getSettingsObserver().getDefaultRefreshRate(), 50, 0.0);
+ assertEquals(director.getSettingsObserver().getDefaultPeakRefreshRate(), 60,
+ 0.0);
+ assertArrayEquals(director.getBrightnessObserver().getHighDisplayBrightnessThreshold(),
+ new int[]{255});
+ assertArrayEquals(director.getBrightnessObserver().getHighAmbientBrightnessThreshold(),
+ new int[]{8000});
+ assertArrayEquals(director.getBrightnessObserver().getLowDisplayBrightnessThreshold(),
+ new int[]{10});
+ assertArrayEquals(director.getBrightnessObserver().getLowAmbientBrightnessThreshold(),
+ new int[]{20});
}
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
@@ -1954,6 +2026,12 @@
String.valueOf(fps));
}
+ void setDefaultPeakRefreshRate(int fps) {
+ putPropertyAndNotify(
+ DeviceConfig.NAMESPACE_DISPLAY_MANAGER, KEY_PEAK_REFRESH_RATE_DEFAULT,
+ String.valueOf(fps));
+ }
+
void setHighDisplayBrightnessThresholds(int[] brightnessThresholds) {
String thresholds = toPropertyValue(brightnessThresholds);
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 90b19a4..6d2631a 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -65,6 +65,7 @@
private final ArgumentCaptor<DeviceState[]> mDeviceStateArrayCaptor = ArgumentCaptor.forClass(
DeviceState[].class);
private final ArgumentCaptor<Integer> mIntegerCaptor = ArgumentCaptor.forClass(Integer.class);
+ private static final int MAX_HINGE_ANGLE_EXCLUSIVE = 360;
private Context mContext;
private SensorManager mSensorManager;
@@ -268,11 +269,7 @@
assertEquals(1, mIntegerCaptor.getValue().intValue());
}
- @Test
- public void create_sensor() throws Exception {
- Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
- when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
-
+ private DeviceStateProviderImpl create_sensorBasedProvider(Sensor sensor) {
String configString = "<device-state-config>\n"
+ " <device-state>\n"
+ " <identifier>1</identifier>\n"
@@ -310,14 +307,22 @@
+ " <name>" + sensor.getName() + "</name>\n"
+ " <value>\n"
+ " <min-inclusive>180</min-inclusive>\n"
+ + " <max>" + MAX_HINGE_ANGLE_EXCLUSIVE + "</max>\n"
+ " </value>\n"
+ " </sensor>\n"
+ " </conditions>\n"
+ " </device-state>\n"
+ "</device-state-config>\n";
DeviceStateProviderImpl.ReadableConfig config = new TestReadableConfig(configString);
- DeviceStateProviderImpl provider = DeviceStateProviderImpl.createFromConfig(mContext,
+ return DeviceStateProviderImpl.createFromConfig(mContext,
config);
+ }
+
+ @Test
+ public void create_sensor() throws Exception {
+ Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
+ DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
provider.setListener(listener);
@@ -371,6 +376,40 @@
}
@Test
+ public void test_invalidSensorValues() throws Exception {
+ // onStateChanged() should not be triggered by invalid sensor values.
+
+ Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
+ when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of(sensor));
+ DeviceStateProviderImpl provider = create_sensorBasedProvider(sensor);
+
+ DeviceStateProvider.Listener listener = mock(DeviceStateProvider.Listener.class);
+ provider.setListener(listener);
+ Mockito.clearInvocations(listener);
+
+ // First, switch to a non-default state.
+ SensorEvent event1 = mock(SensorEvent.class);
+ event1.sensor = sensor;
+ FieldSetter.setField(event1, event1.getClass().getField("values"), new float[]{90});
+ provider.onSensorChanged(event1);
+ verify(listener).onStateChanged(mIntegerCaptor.capture());
+ assertEquals(2, mIntegerCaptor.getValue().intValue());
+
+ Mockito.clearInvocations(listener);
+
+ // Then, send an invalid sensor event, verify that onStateChanged() is not triggered.
+ SensorEvent event2 = mock(SensorEvent.class);
+ event2.sensor = sensor;
+ FieldSetter.setField(event2, event2.getClass().getField("values"),
+ new float[]{MAX_HINGE_ANGLE_EXCLUSIVE});
+
+ provider.onSensorChanged(event2);
+
+ verify(listener, never()).onSupportedDeviceStatesChanged(mDeviceStateArrayCaptor.capture());
+ verify(listener, never()).onStateChanged(mIntegerCaptor.capture());
+ }
+
+ @Test
public void create_invalidSensor() throws Exception {
Sensor sensor = newSensor("sensor", Sensor.STRING_TYPE_HINGE_ANGLE);
when(mSensorManager.getSensorList(anyInt())).thenReturn(List.of());
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index f5ed41a..a42d009 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -375,6 +375,18 @@
mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED));
}
+ private void setBatteryLevel(int batteryLevel) {
+ when(mBatteryManagerInternalMock.getBatteryLevel())
+ .thenReturn(batteryLevel);
+ mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED));
+ }
+
+ private void setBatteryHealth(int batteryHealth) {
+ when(mBatteryManagerInternalMock.getBatteryHealth())
+ .thenReturn(batteryHealth);
+ mBatteryReceiver.onReceive(mContextSpy, new Intent(Intent.ACTION_BATTERY_CHANGED));
+ }
+
private void setAttentiveTimeout(int attentiveTimeoutMillis) {
Settings.Secure.putInt(
mContextSpy.getContentResolver(), Settings.Secure.ATTENTIVE_TIMEOUT,
@@ -399,6 +411,12 @@
.thenReturn(disable);
}
+ private void setDreamsBatteryLevelDrainConfig(int threshold) {
+ when(mResourcesSpy.getInteger(
+ com.android.internal.R.integer.config_dreamsBatteryLevelDrainCutoff)).thenReturn(
+ threshold);
+ }
+
private void advanceTime(long timeMs) {
mClock.fastForward(timeMs);
mTestLooper.dispatchAll();
@@ -619,17 +637,42 @@
}
/**
- * Tests that dreaming continues when undocking and configured to do so.
+ * Tests that dreaming stops when undocking and not configured to keep dreaming.
*/
@Test
- public void testWakefulnessDream_shouldKeepDreamingWhenUndocked() {
+ public void testWakefulnessDream_shouldStopDreamingWhenUndocked_whenNotConfigured() {
+ // Make sure "unplug turns on screen" is configured to true.
+ when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen))
+ .thenReturn(true);
+ when(mDreamManagerInternalMock.keepDreamingWhenUndockedDefault()).thenReturn(false);
+
createService();
startSystem();
- when(mResourcesSpy.getBoolean(
- com.android.internal.R.bool.config_keepDreamingWhenUndocking))
+ when(mBatteryManagerInternalMock.getPlugType())
+ .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
+ setPluggedIn(true);
+
+ forceAwake(); // Needs to be awake first before it can dream.
+ forceDream();
+ when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0);
+ setPluggedIn(false);
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+ /**
+ * Tests that dreaming continues when undocking and configured to do so.
+ */
+ @Test
+ public void testWakefulnessDream_shouldKeepDreamingWhenUndocked_whenConfigured() {
+ // Make sure "unplug turns on screen" is configured to true.
+ when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen))
.thenReturn(true);
- mService.readConfigurationLocked();
+ when(mDreamManagerInternalMock.keepDreamingWhenUndockedDefault()).thenReturn(true);
+
+ createService();
+ startSystem();
when(mBatteryManagerInternalMock.getPlugType())
.thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -643,6 +686,37 @@
assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
}
+ /**
+ * Tests that dreaming stops when undocking while showing a dream that prevents it.
+ */
+ @Test
+ public void testWakefulnessDream_shouldStopDreamingWhenUndocked_whenDreamPrevents() {
+ // Make sure "unplug turns on screen" is configured to true.
+ when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen))
+ .thenReturn(true);
+ when(mDreamManagerInternalMock.keepDreamingWhenUndockedDefault()).thenReturn(true);
+
+ createService();
+ startSystem();
+
+ ArgumentCaptor<DreamManagerInternal.DreamManagerStateListener> dreamManagerStateListener =
+ ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
+ verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
+ dreamManagerStateListener.capture());
+
+ when(mBatteryManagerInternalMock.getPlugType())
+ .thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
+ setPluggedIn(true);
+
+ forceAwake(); // Needs to be awake first before it can dream.
+ forceDream();
+ dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false);
+ when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0);
+ setPluggedIn(false);
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
@Test
public void testWakefulnessDoze_goToSleep() {
createService();
@@ -882,6 +956,41 @@
}
@Test
+ public void testBatteryDrainDuringDream() {
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP, 1);
+ Settings.Secure.putInt(mContextSpy.getContentResolver(),
+ Settings.Secure.SCREENSAVER_ENABLED, 1);
+
+ setMinimumScreenOffTimeoutConfig(100);
+ setDreamsBatteryLevelDrainConfig(5);
+ createService();
+ startSystem();
+
+ doAnswer(inv -> {
+ when(mDreamManagerInternalMock.isDreaming()).thenReturn(true);
+ return null;
+ }).when(mDreamManagerInternalMock).startDream(anyBoolean(), anyString());
+
+ setBatteryLevel(100);
+ setPluggedIn(true);
+
+ forceAwake(); // Needs to be awake first before it can dream.
+ forceDream();
+ advanceTime(10); // Allow async calls to happen
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_DREAMING);
+ setBatteryLevel(90);
+ advanceTime(10); // Allow async calls to happen
+ assertThat(mService.getDreamsBatteryLevelDrain()).isEqualTo(10);
+
+ // If battery overheat protection is enabled, we shouldn't count battery drain
+ setBatteryHealth(BatteryManager.BATTERY_HEALTH_OVERHEAT);
+ setBatteryLevel(70);
+ advanceTime(10); // Allow async calls to happen
+ assertThat(mService.getDreamsBatteryLevelDrain()).isEqualTo(10);
+ }
+
+ @Test
public void testSetDozeOverrideFromDreamManager_triggersSuspendBlocker() {
final String suspendBlockerName = "PowerManagerService.Display";
final String tag = "acq_causes_wakeup";
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index ff5622f..d3e638f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3091,6 +3091,17 @@
assertTrue(activity.mVisibleRequested);
assertTrue(activity.mDisplayContent.mOpeningApps.contains(activity));
assertFalse(activity.mDisplayContent.mClosingApps.contains(activity));
+
+ // There should still be animation (add to opening) if keyguard is going away while the
+ // screen is off because it will be visible after screen is turned on by unlocking.
+ mDisplayContent.mOpeningApps.remove(activity);
+ mDisplayContent.mClosingApps.remove(activity);
+ activity.commitVisibility(false /* visible */, false /* performLayout */);
+ mDisplayContent.getDisplayPolicy().screenTurnedOff();
+ final KeyguardController controller = mSupervisor.getKeyguardController();
+ doReturn(true).when(controller).isKeyguardGoingAway(anyInt());
+ activity.setVisibility(true);
+ assertTrue(mDisplayContent.mOpeningApps.contains(activity));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 0332c4b..43e79f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -25,6 +25,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_ACTIVITY_OPEN;
import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_DREAM_ACTIVITY_OPEN;
@@ -56,6 +57,7 @@
import static org.mockito.Mockito.verify;
import android.annotation.Nullable;
+import android.graphics.Rect;
import android.gui.DropInputMode;
import android.os.Binder;
import android.os.IBinder;
@@ -918,7 +920,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -935,11 +937,77 @@
}
@Test
+ public void testOverrideTaskFragmentAdapter_noOverrideWithOnlyTaskFragmentFillingTask() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord closingActivity = createActivityRecord(task);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+
+ // Make sure the TaskFragment is not embedded.
+ assertFalse(taskFragment.isEmbeddedWithBoundsOverride());
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(closingActivity);
+ prepareActivityForAppTransition(openingActivity);
+ final int uid = 12345;
+ closingActivity.info.applicationInfo.uid = uid;
+ openingActivity.info.applicationInfo.uid = uid;
+ task.effectiveUid = uid;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity,
+ null /* changingTaskFragment */);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+
+ // Animation is not run by the remote handler because the activity is filling the Task.
+ assertFalse(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
+ public void testOverrideTaskFragmentAdapter_overrideWithTaskFragmentNotFillingTask() {
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord closingActivity = createActivityRecord(task);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
+
+ // Create a TaskFragment with embedded activity.
+ final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
+
+ // Make sure the TaskFragment is embedded.
+ taskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect embeddedBounds = new Rect(task.getBounds());
+ embeddedBounds.right = embeddedBounds.left + embeddedBounds.width() / 2;
+ taskFragment.setBounds(embeddedBounds);
+ assertTrue(taskFragment.isEmbeddedWithBoundsOverride());
+ final ActivityRecord openingActivity = taskFragment.getTopMostActivity();
+ prepareActivityForAppTransition(closingActivity);
+ prepareActivityForAppTransition(openingActivity);
+ final int uid = 12345;
+ closingActivity.info.applicationInfo.uid = uid;
+ openingActivity.info.applicationInfo.uid = uid;
+ task.effectiveUid = uid;
+ spyOn(mDisplayContent.mAppTransition);
+
+ // Prepare and start transition.
+ prepareAndTriggerAppTransition(openingActivity, closingActivity,
+ null /* changingTaskFragment */);
+ mWm.mAnimator.executeAfterPrepareSurfacesRunnables();
+
+ // Animation run by the remote handler.
+ assertTrue(remoteAnimationRunner.isAnimationStarted());
+ }
+
+ @Test
public void testOverrideTaskFragmentAdapter_overrideWithNonEmbeddedActivity() {
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Closing non-embedded activity.
final ActivityRecord closingActivity = createActivityRecord(task);
@@ -964,7 +1032,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Closing TaskFragment with embedded activity.
final TaskFragment taskFragment1 = createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -991,7 +1059,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Closing activity in Task1.
final ActivityRecord closingActivity = createActivityRecord(mDisplayContent);
@@ -1015,7 +1083,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Closing TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -1043,7 +1111,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activity.
final TaskFragment taskFragment = createTaskFragmentWithEmbeddedActivity(task, organizer);
@@ -1069,7 +1137,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with embedded activities, one is trusted embedded, and the other
// one is untrusted embedded.
@@ -1128,7 +1196,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with only trusted embedded activity
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
@@ -1168,7 +1236,7 @@
final Task task = createTask(mDisplayContent);
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
final TestRemoteAnimationRunner remoteAnimationRunner = new TestRemoteAnimationRunner();
- setupTaskFragmentRemoteAnimation(organizer, task.mTaskId, remoteAnimationRunner);
+ setupTaskFragmentRemoteAnimation(organizer, remoteAnimationRunner);
// Create a TaskFragment with only trusted embedded activity
final TaskFragment taskFragment = new TaskFragmentBuilder(mAtm)
@@ -1259,7 +1327,7 @@
}
/** Registers remote animation for the organizer. */
- private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer, int taskId,
+ private void setupTaskFragmentRemoteAnimation(TaskFragmentOrganizer organizer,
TestRemoteAnimationRunner remoteAnimationRunner) {
final RemoteAnimationAdapter adapter = new RemoteAnimationAdapter(
remoteAnimationRunner, 10, 1);
@@ -1268,9 +1336,10 @@
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CHANGE, adapter);
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_OPEN, adapter);
definition.addRemoteAnimation(TRANSIT_OLD_TASK_FRAGMENT_CLOSE, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_OPEN, adapter);
+ definition.addRemoteAnimation(TRANSIT_OLD_ACTIVITY_CLOSE, adapter);
mAtm.mTaskFragmentOrganizerController.registerOrganizer(iOrganizer);
- mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, taskId,
- definition);
+ mAtm.mTaskFragmentOrganizerController.registerRemoteAnimations(iOrganizer, definition);
}
private static ITaskFragmentOrganizer getITaskFragmentOrganizer(
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 32c95fa..8cfe503 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -49,6 +49,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
@@ -67,11 +68,14 @@
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.view.WindowManager;
+import android.view.animation.Animation;
import android.window.ITaskFragmentOrganizer;
import android.window.TaskFragmentOrganizer;
import androidx.test.filters.SmallTest;
+import com.android.internal.policy.TransitionAnimation;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -495,6 +499,80 @@
assertEquals(startBounds, taskFragment.mSurfaceFreezer.mFreezeBounds);
}
+ @Test
+ public void testGetNextAppTransitionBackgroundColor() {
+ assumeFalse(WindowManagerService.sEnableShellTransitions);
+
+ // No override by default.
+ assertEquals(0, mDc.mAppTransition.getNextAppTransitionBackgroundColor());
+
+ // Override with a custom color.
+ mDc.mAppTransition.prepareAppTransition(TRANSIT_OPEN, 0);
+ final int testColor = 123;
+ mDc.mAppTransition.overridePendingAppTransition("testPackage", 0 /* enterAnim */,
+ 0 /* exitAnim */, testColor, null /* startedCallback */, null /* endedCallback */,
+ false /* overrideTaskTransaction */);
+
+ assertEquals(testColor, mDc.mAppTransition.getNextAppTransitionBackgroundColor());
+ assertTrue(mDc.mAppTransition.isNextAppTransitionOverrideRequested());
+
+ // Override with ActivityEmbedding remote animation. Background color should be kept.
+ mDc.mAppTransition.overridePendingAppTransitionRemote(mock(RemoteAnimationAdapter.class),
+ false /* sync */, true /* isActivityEmbedding */);
+
+ assertEquals(testColor, mDc.mAppTransition.getNextAppTransitionBackgroundColor());
+ assertFalse(mDc.mAppTransition.isNextAppTransitionOverrideRequested());
+
+ // Background color should not be cleared anymore after #clear().
+ mDc.mAppTransition.clear();
+ assertEquals(0, mDc.mAppTransition.getNextAppTransitionBackgroundColor());
+ assertFalse(mDc.mAppTransition.isNextAppTransitionOverrideRequested());
+ }
+
+ @Test
+ public void testGetNextAppRequestedAnimation() {
+ assumeFalse(WindowManagerService.sEnableShellTransitions);
+ final String packageName = "testPackage";
+ final int enterAnimResId = 1;
+ final int exitAnimResId = 2;
+ final int testColor = 123;
+ final Animation enterAnim = mock(Animation.class);
+ final Animation exitAnim = mock(Animation.class);
+ final TransitionAnimation transitionAnimation = mDc.mAppTransition.mTransitionAnimation;
+ spyOn(transitionAnimation);
+ doReturn(enterAnim).when(transitionAnimation)
+ .loadAppTransitionAnimation(packageName, enterAnimResId);
+ doReturn(exitAnim).when(transitionAnimation)
+ .loadAppTransitionAnimation(packageName, exitAnimResId);
+
+ // No override by default.
+ assertNull(mDc.mAppTransition.getNextAppRequestedAnimation(true /* enter */));
+ assertNull(mDc.mAppTransition.getNextAppRequestedAnimation(false /* enter */));
+
+ // Override with a custom animation.
+ mDc.mAppTransition.prepareAppTransition(TRANSIT_OPEN, 0);
+ mDc.mAppTransition.overridePendingAppTransition(packageName, enterAnimResId, exitAnimResId,
+ testColor, null /* startedCallback */, null /* endedCallback */,
+ false /* overrideTaskTransaction */);
+
+ assertEquals(enterAnim, mDc.mAppTransition.getNextAppRequestedAnimation(true /* enter */));
+ assertEquals(exitAnim, mDc.mAppTransition.getNextAppRequestedAnimation(false /* enter */));
+ assertTrue(mDc.mAppTransition.isNextAppTransitionOverrideRequested());
+
+ // Override with ActivityEmbedding remote animation. Custom animation should be kept.
+ mDc.mAppTransition.overridePendingAppTransitionRemote(mock(RemoteAnimationAdapter.class),
+ false /* sync */, true /* isActivityEmbedding */);
+
+ assertEquals(enterAnim, mDc.mAppTransition.getNextAppRequestedAnimation(true /* enter */));
+ assertEquals(exitAnim, mDc.mAppTransition.getNextAppRequestedAnimation(false /* enter */));
+ assertFalse(mDc.mAppTransition.isNextAppTransitionOverrideRequested());
+
+ // Custom animation should not be cleared anymore after #clear().
+ mDc.mAppTransition.clear();
+ assertNull(mDc.mAppTransition.getNextAppRequestedAnimation(true /* enter */));
+ assertNull(mDc.mAppTransition.getNextAppRequestedAnimation(false /* enter */));
+ }
+
private class TestRemoteAnimationRunner implements IRemoteAnimationRunner {
boolean mCancelled = false;
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index c535182..2b49314 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -474,13 +474,13 @@
@Test
public void testRegisterRemoteAnimations() {
- mController.registerRemoteAnimations(mIOrganizer, TASK_ID, mDefinition);
+ mController.registerRemoteAnimations(mIOrganizer, mDefinition);
- assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer, TASK_ID));
+ assertEquals(mDefinition, mController.getRemoteAnimationDefinition(mIOrganizer));
- mController.unregisterRemoteAnimations(mIOrganizer, TASK_ID);
+ mController.unregisterRemoteAnimations(mIOrganizer);
- assertNull(mController.getRemoteAnimationDefinition(mIOrganizer, TASK_ID));
+ assertNull(mController.getRemoteAnimationDefinition(mIOrganizer));
}
@Test
@@ -822,6 +822,21 @@
}
@Test
+ public void testOnTransactionHandled_skipTransactionForUnregisterOrganizer() {
+ mController.unregisterOrganizer(mIOrganizer);
+ final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent);
+ final IBinder fragmentToken = new Binder();
+
+ // Allow organizer to create TaskFragment and start/reparent activity to TaskFragment.
+ createTaskFragmentFromOrganizer(mTransaction, ownerActivity, fragmentToken);
+ mController.onTransactionHandled(new Binder(), mTransaction,
+ getTransitionType(mTransaction), false /* shouldApplyIndependently */);
+
+ // Nothing should happen as the organizer is not registered.
+ assertNull(mWindowOrganizerController.getTaskFragment(fragmentToken));
+ }
+
+ @Test
public void testOrganizerRemovedWithPendingEvents() {
final TaskFragment tf0 = new TaskFragmentBuilder(mAtm)
.setCreateParentTask()
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index a6c5fd8..1188f49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -85,6 +85,11 @@
private static final Rect DISPLAY_STABLE_BOUNDS = new Rect(/* left */ 100,
/* top */ 200, /* right */ 1620, /* bottom */ 680);
+ private static final Rect SMALL_DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
+ /* right */ 1000, /* bottom */ 500);
+ private static final Rect SMALL_DISPLAY_STABLE_BOUNDS = new Rect(/* left */ 100,
+ /* top */ 50, /* right */ 900, /* bottom */ 450);
+
private ActivityRecord mActivity;
private TaskLaunchParamsModifier mTarget;
@@ -571,6 +576,29 @@
}
@Test
+ public void testBoundsInOptionsInfersFullscreenWithBoundsOnFreeformSupportFullscreenDisplay() {
+ final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FULLSCREEN);
+ mAtm.mTaskSupervisor.mService.mSupportsFreeformWindowManagement = true;
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ final Rect expectedBounds = new Rect(0, 0, 100, 100);
+ options.setLaunchBounds(expectedBounds);
+
+ mCurrent.mPreferredTaskDisplayArea = fullscreenDisplay.getDefaultTaskDisplayArea();
+
+ assertEquals(RESULT_CONTINUE,
+ new CalculateRequestBuilder().setOptions(options).calculate());
+
+ // Setting bounds shouldn't lead to freeform windowing mode on fullscreen display by
+ // default (even with freeform support), but we need to check here if the bounds is set even
+ // with fullscreen windowing mode in case it's restored later.
+ assertEquivalentWindowingMode(WINDOWING_MODE_FULLSCREEN, mResult.mWindowingMode,
+ WINDOWING_MODE_FULLSCREEN);
+ assertEquals(expectedBounds, mResult.mBounds);
+ }
+
+ @Test
public void testInheritsFreeformModeFromSourceOnFullscreenDisplay() {
final TestDisplayContent fullscreenDisplay = createNewDisplayContent(
WINDOWING_MODE_FULLSCREEN);
@@ -952,6 +980,8 @@
WINDOWING_MODE_FULLSCREEN);
final ActivityRecord source = createSourceActivity(fullscreenDisplay);
source.getTask().setWindowingMode(WINDOWING_MODE_FREEFORM);
+ // Set some bounds to avoid conflict with the other activity.
+ source.setBounds(100, 100, 200, 200);
final ActivityOptions options = ActivityOptions.makeBasic();
final Rect expected = new Rect(0, 0, 150, 150);
@@ -1321,6 +1351,20 @@
}
@Test
+ public void testDefaultFreeformSizeShrinksOnSmallDisplay() {
+ final TestDisplayContent freeformDisplay = createNewDisplayContent(
+ WINDOWING_MODE_FREEFORM, SMALL_DISPLAY_BOUNDS, SMALL_DISPLAY_STABLE_BOUNDS);
+
+ final ActivityOptions options = ActivityOptions.makeBasic();
+ options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+
+ assertEquals(RESULT_CONTINUE, new CalculateRequestBuilder().setOptions(options)
+ .calculate());
+
+ assertEquals(new Rect(414, 77, 587, 423), mResult.mBounds);
+ }
+
+ @Test
public void testDefaultFreeformSizeRespectsMinAspectRatio() {
final TestDisplayContent freeformDisplay = createNewDisplayContent(
WINDOWING_MODE_FREEFORM);
@@ -1510,16 +1554,15 @@
options.setLaunchDisplayId(freeformDisplay.mDisplayId);
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(100, 300, 1820, 1380);
+ mCurrent.mBounds.set(0, 0, 3000, 2000);
mActivity.info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setOptions(options).calculate());
- assertTrue("Result bounds should start from app bounds's origin, but it's "
- + mResult.mBounds,
- mResult.mBounds.left == 100 && mResult.mBounds.top == 200);
+ // Must shrink to fit the display while reserving aspect ratio.
+ assertEquals(new Rect(127, 227, 766, 653), mResult.mBounds);
}
@Test
@@ -1535,18 +1578,19 @@
final ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(freeformDisplay.mDisplayId);
+ final ActivityInfo.WindowLayout layout = new WindowLayoutBuilder()
+ .setMinWidth(500).setMinHeight(500).build();
mCurrent.mWindowingMode = WINDOWING_MODE_FREEFORM;
- mCurrent.mBounds.set(100, 300, 1820, 1380);
+ mCurrent.mBounds.set(0, 0, 2000, 3000);
mActivity.info.applicationInfo.targetSdkVersion = Build.VERSION_CODES.LOLLIPOP;
assertEquals(RESULT_CONTINUE,
- new CalculateRequestBuilder().setOptions(options).calculate());
+ new CalculateRequestBuilder().setOptions(options).setLayout(layout).calculate());
- assertTrue("Result bounds should start from top-right corner of app bounds, but "
- + "it's " + mResult.mBounds,
- mResult.mBounds.left == -100 && mResult.mBounds.top == 200);
+ // Must shrink to fit the display while reserving aspect ratio.
+ assertEquals(new Rect(1093, 227, 1593, 727), mResult.mBounds);
}
@Test
@@ -1721,7 +1765,7 @@
assertEquals(RESULT_CONTINUE,
new CalculateRequestBuilder().setOptions(options).calculate());
- assertEquals(new Rect(100, 200, 400, 500), mResult.mBounds);
+ assertEquals(new Rect(127, 227, 427, 527), mResult.mBounds);
}
@Test
@@ -1774,13 +1818,18 @@
}
private TestDisplayContent createNewDisplayContent(int windowingMode) {
+ return createNewDisplayContent(windowingMode, DISPLAY_BOUNDS, DISPLAY_STABLE_BOUNDS);
+ }
+
+ private TestDisplayContent createNewDisplayContent(int windowingMode, Rect displayBounds,
+ Rect displayStableBounds) {
final TestDisplayContent display = addNewDisplayContentAt(DisplayContent.POSITION_TOP);
display.getDefaultTaskDisplayArea().setWindowingMode(windowingMode);
- display.setBounds(DISPLAY_BOUNDS);
+ display.setBounds(displayBounds);
display.getConfiguration().densityDpi = DENSITY_DEFAULT;
display.getConfiguration().orientation = ORIENTATION_LANDSCAPE;
configInsetsState(display.getInsetsStateController().getRawInsetsState(), display,
- DISPLAY_STABLE_BOUNDS);
+ displayStableBounds);
return display;
}