Merge "[Panlingual] Restore per-app locales for an archived application" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 0ccdf37..ab5d503 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -1043,20 +1043,12 @@
name: "device_policy_aconfig_flags",
package: "android.app.admin.flags",
container: "system",
- exportable: true,
srcs: [
"core/java/android/app/admin/flags/flags.aconfig",
],
}
java_aconfig_library {
- name: "device_policy_exported_aconfig_flags_lib",
- aconfig_declarations: "device_policy_aconfig_flags",
- defaults: ["framework-minus-apex-aconfig-java-defaults"],
- mode: "exported",
-}
-
-java_aconfig_library {
name: "device_policy_aconfig_flags_lib",
aconfig_declarations: "device_policy_aconfig_flags",
defaults: ["framework-minus-apex-aconfig-java-defaults"],
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4ac6bac..604b37d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -114,6 +114,7 @@
import com.android.internal.graphics.ColorUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ContrastColorUtil;
+import com.android.internal.util.NewlineNormalizer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -3190,7 +3191,7 @@
return charSequence;
}
- return charSequence.toString().replaceAll("[\r\n]+", "\n");
+ return NewlineNormalizer.normalizeNewlines(charSequence.toString());
}
private static CharSequence removeTextSizeSpans(CharSequence charSequence) {
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index efd5a45..ef8501f 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -351,6 +351,12 @@
}
/** @hide */
+ public boolean isFreeform() {
+ return configuration.windowConfiguration.getWindowingMode()
+ == WindowConfiguration.WINDOWING_MODE_FREEFORM;
+ }
+
+ /** @hide */
@WindowConfiguration.ActivityType
public int getActivityType() {
return configuration.windowConfiguration.getActivityType();
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 46c9e78..9ef8b38 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -21,7 +21,6 @@
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -177,10 +176,6 @@
* provisioned into "affiliated" mode when on a Headless System User Mode device.
*
* <p>This mode adds a Profile Owner to all users other than the user the Device Owner is on.
- *
- * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
- * DPCs should set the value of attribute "headless-device-owner-mode" inside the
- * "headless-system-user" tag as "affiliated".
*/
public static final int HEADLESS_DEVICE_OWNER_MODE_AFFILIATED = 1;
@@ -190,10 +185,6 @@
*
* <p>This mode only allows a single secondary user on the device blocking the creation of
* additional secondary users.
- *
- * <p>Starting from Android version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
- * DPCs should set the value of attribute "headless-device-owner-mode" inside the
- * "headless-system-user" tag as "single_user".
*/
@FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
@@ -392,30 +383,17 @@
}
mSupportsTransferOwnership = true;
} else if (tagName.equals("headless-system-user")) {
- String deviceOwnerModeStringValue = null;
- if (Flags.headlessSingleUserCompatibilityFix()) {
- deviceOwnerModeStringValue = parser.getAttributeValue(
- null, "headless-device-owner-mode");
- }
- if (deviceOwnerModeStringValue == null) {
- deviceOwnerModeStringValue =
- parser.getAttributeValue(null, "device-owner-mode");
- }
+ String deviceOwnerModeStringValue =
+ parser.getAttributeValue(null, "device-owner-mode");
- if ("unsupported".equalsIgnoreCase(deviceOwnerModeStringValue)) {
+ if (deviceOwnerModeStringValue.equalsIgnoreCase("unsupported")) {
mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
- } else if ("affiliated".equalsIgnoreCase(deviceOwnerModeStringValue)) {
+ } else if (deviceOwnerModeStringValue.equalsIgnoreCase("affiliated")) {
mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
- } else if ("single_user".equalsIgnoreCase(deviceOwnerModeStringValue)) {
+ } else if (deviceOwnerModeStringValue.equalsIgnoreCase("single_user")) {
mHeadlessDeviceOwnerMode = HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
} else {
- if (Flags.headlessSingleUserCompatibilityFix()) {
- Log.e(TAG, "Unknown headless-system-user mode: "
- + deviceOwnerModeStringValue);
- } else {
- throw new XmlPullParserException(
- "headless-system-user mode must be valid");
- }
+ throw new XmlPullParserException("headless-system-user mode must be valid");
}
}
}
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 83daa45..18914e1 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -303,24 +303,3 @@
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- name: "headless_single_user_compatibility_fix"
- namespace: "enterprise"
- description: "Fix for compatibility issue introduced from using single_user mode on pre-Android V builds"
- bug: "338050276"
- is_exported: true
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
- name: "headless_single_min_target_sdk"
- namespace: "enterprise"
- description: "Only allow DPCs targeting Android V to provision into single user mode"
- bug: "338588825"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e6ddf35..009713f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5123,13 +5123,6 @@
public static final String SCREEN_BRIGHTNESS = "screen_brightness";
/**
- * The screen backlight brightness between 0.0f and 1.0f.
- * @hide
- */
- @Readable
- public static final String SCREEN_BRIGHTNESS_FLOAT = "screen_brightness_float";
-
- /**
* Control whether to enable automatic brightness mode.
*/
@Readable
@@ -6273,7 +6266,6 @@
PUBLIC_SETTINGS.add(DIM_SCREEN);
PUBLIC_SETTINGS.add(SCREEN_OFF_TIMEOUT);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS);
- PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_FLOAT);
PUBLIC_SETTINGS.add(SCREEN_BRIGHTNESS_MODE);
PUBLIC_SETTINGS.add(MODE_RINGER_STREAMS_AFFECTED);
PUBLIC_SETTINGS.add(MUTE_STREAMS_AFFECTED);
diff --git a/core/java/com/android/internal/util/NewlineNormalizer.java b/core/java/com/android/internal/util/NewlineNormalizer.java
new file mode 100644
index 0000000..0104d1f
--- /dev/null
+++ b/core/java/com/android/internal/util/NewlineNormalizer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 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.internal.util;
+
+
+import java.util.regex.Pattern;
+
+/**
+ * Utility class that replaces consecutive empty lines with single new line.
+ * @hide
+ */
+public class NewlineNormalizer {
+
+ private static final Pattern MULTIPLE_NEWLINES = Pattern.compile("\\v(\\s*\\v)?");
+
+ // Private constructor to prevent instantiation
+ private NewlineNormalizer() {}
+
+ /**
+ * Replaces consecutive newlines with a single newline in the input text.
+ */
+ public static String normalizeNewlines(String text) {
+ return MULTIPLE_NEWLINES.matcher(text).replaceAll("\n");
+ }
+}
diff --git a/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java b/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
new file mode 100644
index 0000000..bcdac61
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/NewlineNormalizerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2024 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.internal.util;
+
+import static junit.framework.Assert.assertEquals;
+
+
+import android.platform.test.annotations.DisabledOnRavenwood;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test for {@link NewlineNormalizer}
+ * @hide
+ */
+@DisabledOnRavenwood(blockedBy = NewlineNormalizer.class)
+@RunWith(AndroidJUnit4.class)
+public class NewlineNormalizerTest {
+
+ @Rule
+ public final RavenwoodRule mRavenwood = new RavenwoodRule();
+
+ @Test
+ public void testEmptyInput() {
+ assertEquals("", NewlineNormalizer.normalizeNewlines(""));
+ }
+
+ @Test
+ public void testSingleNewline() {
+ assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n"));
+ }
+
+ @Test
+ public void testMultipleConsecutiveNewlines() {
+ assertEquals("\n", NewlineNormalizer.normalizeNewlines("\n\n\n\n\n"));
+ }
+
+ @Test
+ public void testNewlinesWithSpacesAndTabs() {
+ String input = "Line 1\n \n \t \n\tLine 2";
+ // Adjusted expected output to include the tab character
+ String expected = "Line 1\n\tLine 2";
+ assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
+ }
+
+ @Test
+ public void testMixedNewlineCharacters() {
+ String input = "Line 1\r\nLine 2\u000BLine 3\fLine 4\u2028Line 5\u2029Line 6";
+ String expected = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6";
+ assertEquals(expected, NewlineNormalizer.normalizeNewlines(input));
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index f195f95..3ab1fad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -81,6 +81,10 @@
super(context, taskInfo, syncQueue, taskListener, displayLayout);
mCallback = callback;
mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
+ if (Flags.enableDesktopWindowingMode() && Flags.enableWindowingDynamicInitialBounds()) {
+ // Don't show the SCM button for freeform tasks
+ mHasSizeCompat &= !taskInfo.isFreeform();
+ }
mCameraCompatControlState =
taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
mCompatUIHintsState = compatUIHintsState;
@@ -136,6 +140,10 @@
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
mHasSizeCompat = taskInfo.appCompatTaskInfo.topActivityInSizeCompat;
+ if (Flags.enableDesktopWindowingMode() && Flags.enableWindowingDynamicInitialBounds()) {
+ // Don't show the SCM button for freeform tasks
+ mHasSizeCompat &= !taskInfo.isFreeform();
+ }
mCameraCompatControlState =
taskInfo.appCompatTaskInfo.cameraCompatTaskInfo.cameraCompatControlState;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
new file mode 100644
index 0000000..6da3741
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+@file:JvmName("DesktopModeUtils")
+
+package com.android.wm.shell.desktopmode
+
+import android.app.ActivityManager.RunningTaskInfo
+import android.content.pm.ActivityInfo.isFixedOrientationLandscape
+import android.content.pm.ActivityInfo.isFixedOrientationPortrait
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.graphics.Rect
+import android.os.SystemProperties
+import android.util.Size
+import com.android.wm.shell.common.DisplayLayout
+
+
+val DESKTOP_MODE_INITIAL_BOUNDS_SCALE: Float = SystemProperties
+ .getInt("persist.wm.debug.desktop_mode_initial_bounds_scale", 75) / 100f
+
+val DESKTOP_MODE_LANDSCAPE_APP_PADDING: Int = SystemProperties
+ .getInt("persist.wm.debug.desktop_mode_landscape_app_padding", 25)
+
+
+/**
+ * Calculates the initial bounds required for an application to fill a scale of the display bounds
+ * without any letterboxing. This is done by taking into account the applications fullscreen size,
+ * aspect ratio, orientation and resizability to calculate an area this is compatible with the
+ * applications previous configuration.
+ */
+fun calculateInitialBounds(
+ displayLayout: DisplayLayout,
+ taskInfo: RunningTaskInfo,
+ scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE
+): Rect {
+ val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height())
+ val appAspectRatio = calculateAspectRatio(taskInfo)
+ val idealSize = calculateIdealSize(screenBounds, scale)
+ // If no top activity exists, apps fullscreen bounds and aspect ratio cannot be calculated.
+ // Instead default to the desired initial bounds.
+ val topActivityInfo = taskInfo.topActivityInfo
+ ?: return positionInScreen(idealSize, screenBounds)
+
+ val initialSize: Size = when (taskInfo.configuration.orientation) {
+ ORIENTATION_LANDSCAPE -> {
+ if (taskInfo.isResizeable) {
+ if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) {
+ // Respect apps fullscreen width
+ Size(taskInfo.appCompatTaskInfo.topActivityLetterboxWidth, idealSize.height)
+ } else {
+ idealSize
+ }
+ } else {
+ maximumSizeMaintainingAspectRatio(taskInfo, idealSize,
+ appAspectRatio)
+ }
+ }
+ ORIENTATION_PORTRAIT -> {
+ val customPortraitWidthForLandscapeApp = screenBounds.width() -
+ (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2)
+ if (taskInfo.isResizeable) {
+ if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+ // Respect apps fullscreen height and apply custom app width
+ Size(customPortraitWidthForLandscapeApp,
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight)
+ } else {
+ idealSize
+ }
+ } else {
+ if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) {
+ // Apply custom app width and calculate maximum size
+ maximumSizeMaintainingAspectRatio(
+ taskInfo,
+ Size(customPortraitWidthForLandscapeApp, idealSize.height),
+ appAspectRatio)
+ } else {
+ maximumSizeMaintainingAspectRatio(taskInfo, idealSize,
+ appAspectRatio)
+ }
+ }
+ }
+ else -> {
+ idealSize
+ }
+ }
+
+ return positionInScreen(initialSize, screenBounds)
+}
+
+/**
+ * Calculates the largest size that can fit in a given area while maintaining a specific aspect
+ * ratio.
+ */
+private fun maximumSizeMaintainingAspectRatio(
+ taskInfo: RunningTaskInfo,
+ targetArea: Size,
+ aspectRatio: Float
+): Size {
+ val targetHeight = targetArea.height
+ val targetWidth = targetArea.width
+ val finalHeight: Int
+ val finalWidth: Int
+ if (isFixedOrientationPortrait(taskInfo.topActivityInfo!!.screenOrientation)) {
+ val tempWidth = (targetHeight / aspectRatio).toInt()
+ if (tempWidth <= targetWidth) {
+ finalHeight = targetHeight
+ finalWidth = tempWidth
+ } else {
+ finalWidth = targetWidth
+ finalHeight = (finalWidth * aspectRatio).toInt()
+ }
+ } else {
+ val tempWidth = (targetHeight * aspectRatio).toInt()
+ if (tempWidth <= targetWidth) {
+ finalHeight = targetHeight
+ finalWidth = tempWidth
+ } else {
+ finalWidth = targetWidth
+ finalHeight = (finalWidth / aspectRatio).toInt()
+ }
+ }
+ return Size(finalWidth, finalHeight)
+}
+
+/**
+ * Calculates the aspect ratio of an activity from its fullscreen bounds.
+ */
+private fun calculateAspectRatio(taskInfo: RunningTaskInfo): Float {
+ if (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed) {
+ val appLetterboxWidth = taskInfo.appCompatTaskInfo.topActivityLetterboxWidth
+ val appLetterboxHeight = taskInfo.appCompatTaskInfo.topActivityLetterboxHeight
+ return maxOf(appLetterboxWidth, appLetterboxHeight) /
+ minOf(appLetterboxWidth, appLetterboxHeight).toFloat()
+ }
+ val appBounds = taskInfo.configuration.windowConfiguration.appBounds ?: return 1f
+ return maxOf(appBounds.height(), appBounds.width()) /
+ minOf(appBounds.height(), appBounds.width()).toFloat()
+}
+
+/**
+ * Calculates the desired initial bounds for applications in desktop windowing. This is done as a
+ * scale of the screen bounds.
+ */
+private fun calculateIdealSize(screenBounds: Rect, scale: Float): Size {
+ val width = (screenBounds.width() * scale).toInt()
+ val height = (screenBounds.height() * scale).toInt()
+ return Size(width, height)
+}
+
+/**
+ * Adjusts bounds to be positioned in the middle of the screen.
+ */
+private fun positionInScreen(desiredSize: Size, screenBounds: Rect): Rect {
+ // TODO(b/325240051): Position apps with bottom heavy offset
+ val heightOffset = (screenBounds.height() - desiredSize.height) / 2
+ val widthOffset = (screenBounds.width() - desiredSize.width) / 2
+ return Rect(widthOffset, heightOffset,
+ desiredSize.width + widthOffset, desiredSize.height + heightOffset)
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index f7bfb86..b0d5923 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -47,6 +47,7 @@
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.window.flags.Flags
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
@@ -85,7 +86,6 @@
import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener
-import com.android.wm.shell.windowdecor.extension.isFreeform
import com.android.wm.shell.windowdecor.extension.isFullscreen
import java.io.PrintWriter
import java.util.Optional
@@ -203,6 +203,11 @@
dragAndDropController.addListener(this)
}
+ @VisibleForTesting
+ fun getVisualIndicator(): DesktopModeVisualIndicator? {
+ return visualIndicator
+ }
+
fun setOnTaskResizeAnimationListener(listener: OnTaskResizeAnimationListener) {
toggleResizeDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
enterDesktopTaskTransitionHandler.setOnTaskResizeAnimationListener(listener)
@@ -605,8 +610,9 @@
}
/**
- * Quick-resizes a desktop task, toggling between the stable bounds and the last saved bounds
- * if available or the default bounds otherwise.
+ * Quick-resizes a desktop task, toggling between a fullscreen state (represented by the
+ * stable bounds) and a free floating state (either the last saved bounds if available or the
+ * default bounds otherwise).
*/
fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
@@ -623,7 +629,11 @@
if (taskBoundsBeforeMaximize != null) {
destinationBounds.set(taskBoundsBeforeMaximize)
} else {
- destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+ if (Flags.enableWindowingDynamicInitialBounds()){
+ destinationBounds.set(calculateInitialBounds(displayLayout, taskInfo))
+ } else {
+ destinationBounds.set(getDefaultDesktopTaskBounds(displayLayout))
+ }
}
} else {
// Save current bounds so that task can be restored back to original bounds if necessary
@@ -1011,6 +1021,7 @@
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo
) {
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!!
val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode
val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) {
@@ -1019,6 +1030,9 @@
} else {
WINDOWING_MODE_FREEFORM
}
+ if (Flags.enableWindowingDynamicInitialBounds()) {
+ wct.setBounds(taskInfo.token, calculateInitialBounds(displayLayout, taskInfo))
+ }
wct.setWindowingMode(taskInfo.token, targetWindowingMode)
wct.reorder(taskInfo.token, true /* onTop */)
if (isDesktopDensityOverrideSet()) {
@@ -1239,13 +1253,17 @@
* @param y height of drag, to be checked against status bar height.
*/
fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) {
- val indicator = visualIndicator ?: return
+ val indicator = getVisualIndicator() ?: return
val indicatorType = indicator
.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
when (indicatorType) {
DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR -> {
val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return
- finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
+ if (Flags.enableWindowingDynamicInitialBounds()) {
+ finalizeDragToDesktop(taskInfo, calculateInitialBounds(displayLayout, taskInfo))
+ } else {
+ finalizeDragToDesktop(taskInfo, getDefaultDesktopTaskBounds(displayLayout))
+ }
}
DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR,
DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index f7516da..4c347ad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -110,6 +110,8 @@
private ResizeVeil mResizeVeil;
private Bitmap mAppIconBitmap;
+ private Bitmap mResizeVeilBitmap;
+
private CharSequence mAppName;
private ExclusionRegionListener mExclusionRegionListener;
@@ -468,11 +470,15 @@
PackageManager pm = mContext.getApplicationContext().getPackageManager();
final IconProvider provider = new IconProvider(mContext);
final Drawable appIconDrawable = provider.getIcon(activityInfo);
- final Resources resources = mContext.getResources();
- final BaseIconFactory factory = new BaseIconFactory(mContext,
- resources.getDisplayMetrics().densityDpi,
- resources.getDimensionPixelSize(R.dimen.desktop_mode_caption_icon_radius));
- mAppIconBitmap = factory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+ final BaseIconFactory headerIconFactory = createIconFactory(mContext,
+ R.dimen.desktop_mode_caption_icon_radius);
+ mAppIconBitmap = headerIconFactory.createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+
+ final BaseIconFactory resizeVeilIconFactory = createIconFactory(mContext,
+ R.dimen.desktop_mode_resize_veil_icon_size);
+ mResizeVeilBitmap = resizeVeilIconFactory
+ .createScaledBitmap(appIconDrawable, MODE_DEFAULT);
+
final ApplicationInfo applicationInfo = activityInfo.applicationInfo;
mAppName = pm.getApplicationLabel(applicationInfo);
} finally {
@@ -480,6 +486,13 @@
}
}
+ private BaseIconFactory createIconFactory(Context context, int dimensions) {
+ final Resources resources = context.getResources();
+ final int densityDpi = resources.getDisplayMetrics().densityDpi;
+ final int iconSize = resources.getDimensionPixelSize(dimensions);
+ return new BaseIconFactory(context, densityDpi, iconSize);
+ }
+
private void closeDragResizeListener() {
if (mDragResizeListener == null) {
return;
@@ -495,7 +508,7 @@
private void createResizeVeilIfNeeded() {
if (mResizeVeil != null) return;
loadAppInfoIfNeeded();
- mResizeVeil = new ResizeVeil(mContext, mDisplayController, mAppIconBitmap, mTaskInfo,
+ mResizeVeil = new ResizeVeil(mContext, mDisplayController, mResizeVeilBitmap, mTaskInfo,
mTaskSurface, mSurfaceControlTransactionSupplier);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
index a2293d5..ec20471 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/extension/TaskInfo.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.windowdecor.extension
import android.app.TaskInfo
-import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.view.WindowInsetsController.APPEARANCE_LIGHT_CAPTION_BARS
import android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND
@@ -36,6 +35,3 @@
val TaskInfo.isFullscreen: Boolean
get() = windowingMode == WINDOWING_MODE_FULLSCREEN
-
-val TaskInfo.isFreeform: Boolean
- get() = windowingMode == WINDOWING_MODE_FREEFORM
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index df8a222..3f76c4f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -24,6 +24,12 @@
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+import android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+import android.content.res.Configuration.ORIENTATION_LANDSCAPE
+import android.content.res.Configuration.ORIENTATION_PORTRAIT
import android.graphics.Point
import android.graphics.PointF
import android.graphics.Rect
@@ -101,7 +107,9 @@
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.quality.Strictness
@@ -141,6 +149,7 @@
@Mock lateinit var dragAndDropController: DragAndDropController
@Mock lateinit var multiInstanceHelper: MultiInstanceHelper
@Mock lateinit var desktopModeLoggerTransitionObserver: DesktopModeLoggerTransitionObserver
+ @Mock lateinit var desktopModeVisualIndicator: DesktopModeVisualIndicator
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -154,6 +163,15 @@
// Mock running tasks are registered here so we can get the list from mock shell task organizer
private val runningTasks = mutableListOf<RunningTaskInfo>()
+ private val DISPLAY_DIMENSION_SHORT = 1600
+ private val DISPLAY_DIMENSION_LONG = 2560
+ private val DEFAULT_LANDSCAPE_BOUNDS = Rect(320, 200, 2240, 1400)
+ private val DEFAULT_PORTRAIT_BOUNDS = Rect(200, 320, 1400, 2240)
+ private val RESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 680, 1575, 1880)
+ private val RESIZABLE_PORTRAIT_BOUNDS = Rect(680, 200, 1880, 1400)
+ private val UNRESIZABLE_LANDSCAPE_BOUNDS = Rect(25, 699, 1575, 1861)
+ private val UNRESIZABLE_PORTRAIT_BOUNDS = Rect(830, 200, 1730, 1400)
+
@Before
fun setUp() {
mockitoSession = mockitoSession().strictness(Strictness.LENIENT)
@@ -161,7 +179,7 @@
whenever(DesktopModeStatus.isEnabled()).thenReturn(true)
doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
- shellInit = Mockito.spy(ShellInit(testExecutor))
+ shellInit = spy(ShellInit(testExecutor))
desktopModeTaskRepository = DesktopModeTaskRepository()
desktopTasksLimiter =
DesktopTasksLimiter(transitions, desktopModeTaskRepository, shellTaskOrganizer)
@@ -464,6 +482,135 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask()
+ setUpLandscapeDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+ setUpLandscapeDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ shouldLetterbox = true)
+ setUpLandscapeDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+ setUpLandscapeDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
+ setUpLandscapeDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+ setUpPortraitDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(isResizable = false,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun moveToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val task = setUpFullscreenTask(isResizable = false,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+ setUpPortraitDisplay()
+
+ controller.moveToDesktop(task)
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() {
val task = setUpFullscreenTask()
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
@@ -1225,6 +1372,185 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_resizable_undefinedOrientation_defaultLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask()
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_resizable_landscapeOrientation_defaultLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_resizable_portraitOrientation_resizablePortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(screenOrientation = SCREEN_ORIENTATION_PORTRAIT,
+ shouldLetterbox = true)
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_unResizable_landscapeOrientation_defaultLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE)
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_landscapeDevice_unResizable_portraitOrientation_unResizablePortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(isResizable = false,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT, shouldLetterbox = true)
+ setUpLandscapeDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_resizable_undefinedOrientation_defaultPortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_resizable_portraitOrientation_defaultPortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_resizable_landscapeOrientation_resizableLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(RESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_unResizable_portraitOrientation_defaultPortraitBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(isResizable = false,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_PORTRAIT)
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(800f, 1280f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(DEFAULT_PORTRAIT_BOUNDS)
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_ENABLE_WINDOWING_DYNAMIC_INITIAL_BOUNDS)
+ fun dragToDesktop_portraitDevice_unResizable_landscapeOrientation_unResizableLandscapeBounds() {
+ doReturn(true).`when` { DesktopModeStatus.isDesktopModeSupported(any()) }
+ val spyController = spy(controller)
+ whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
+ whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull(), anyOrNull()))
+ .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_DESKTOP_INDICATOR)
+
+ val task = setUpFullscreenTask(isResizable = false,
+ deviceOrientation = ORIENTATION_PORTRAIT,
+ screenOrientation = SCREEN_ORIENTATION_LANDSCAPE, shouldLetterbox = true)
+ setUpPortraitDisplay()
+
+ spyController.onDragPositioningEndThroughStatusBar(PointF(200f, 200f), task)
+ val wct = getLatestDragToDesktopWct()
+ assertThat(findBoundsChange(wct, task)).isEqualTo(UNRESIZABLE_LANDSCAPE_BOUNDS)
+ }
+
+ @Test
fun onDesktopDragMove_endsOutsideValidDragArea_snapsToValidBounds() {
val task = setUpFreeformTask()
val mockSurface = mock(SurfaceControl::class.java)
@@ -1276,8 +1602,7 @@
controller.toggleDesktopTaskSize(task)
// Assert bounds set to stable bounds
val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds)
- .isEqualTo(STABLE_BOUNDS)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS)
}
@Test
@@ -1304,8 +1629,7 @@
// Assert bounds set to last bounds before maximize
val wct = getLatestToggleResizeDesktopTaskWct()
- assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds)
- .isEqualTo(boundsBeforeMaximize)
+ assertThat(findBoundsChange(wct, task)).isEqualTo(boundsBeforeMaximize)
}
@Test
@@ -1346,14 +1670,65 @@
return task
}
- private fun setUpFullscreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
+ private fun setUpFullscreenTask(
+ displayId: Int = DEFAULT_DISPLAY,
+ isResizable: Boolean = true,
+ windowingMode: Int = WINDOWING_MODE_FULLSCREEN,
+ deviceOrientation: Int = ORIENTATION_LANDSCAPE,
+ screenOrientation: Int = SCREEN_ORIENTATION_UNSPECIFIED,
+ shouldLetterbox: Boolean = false
+ ): RunningTaskInfo {
val task = createFullscreenTask(displayId)
+ val activityInfo = ActivityInfo()
+ activityInfo.screenOrientation = screenOrientation
+ with(task) {
+ topActivityInfo = activityInfo
+ isResizeable = isResizable
+ configuration.orientation = deviceOrientation
+ configuration.windowConfiguration.windowingMode = windowingMode
+
+ if (shouldLetterbox) {
+ if (deviceOrientation == ORIENTATION_LANDSCAPE &&
+ screenOrientation == SCREEN_ORIENTATION_PORTRAIT) {
+ // Letterbox to portrait size
+ appCompatTaskInfo.topActivityBoundsLetterboxed = true
+ appCompatTaskInfo.topActivityLetterboxWidth = 1200
+ appCompatTaskInfo.topActivityLetterboxHeight = 1600
+ } else if (deviceOrientation == ORIENTATION_PORTRAIT &&
+ screenOrientation == SCREEN_ORIENTATION_LANDSCAPE) {
+ // Letterbox to landscape size
+ appCompatTaskInfo.topActivityBoundsLetterboxed = true
+ appCompatTaskInfo.topActivityLetterboxWidth = 1600
+ appCompatTaskInfo.topActivityLetterboxHeight = 1200
+ }
+ } else {
+ appCompatTaskInfo.topActivityBoundsLetterboxed = false
+ }
+
+ if (deviceOrientation == ORIENTATION_LANDSCAPE) {
+ configuration.windowConfiguration.appBounds = Rect(0, 0,
+ DISPLAY_DIMENSION_LONG, DISPLAY_DIMENSION_SHORT)
+ } else {
+ configuration.windowConfiguration.appBounds = Rect(0, 0,
+ DISPLAY_DIMENSION_SHORT, DISPLAY_DIMENSION_LONG)
+ }
+ }
whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
runningTasks.add(task)
return task
}
+ private fun setUpLandscapeDisplay() {
+ whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_LONG)
+ whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_SHORT)
+ }
+
+ private fun setUpPortraitDisplay() {
+ whenever(displayLayout.width()).thenReturn(DISPLAY_DIMENSION_SHORT)
+ whenever(displayLayout.height()).thenReturn(DISPLAY_DIMENSION_LONG)
+ }
+
private fun setUpSplitScreenTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createSplitScreenTask(displayId)
whenever(DesktopModeStatus.enforceDeviceRestrictions()).thenReturn(true)
@@ -1418,6 +1793,17 @@
return arg.value
}
+ private fun getLatestDragToDesktopWct(): WindowContainerTransaction {
+ val arg: ArgumentCaptor<WindowContainerTransaction> =
+ ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
+ if (ENABLE_SHELL_TRANSITIONS) {
+ verify(dragToDesktopTransitionHandler).finishDragToDesktopTransition(capture(arg))
+ } else {
+ verify(shellTaskOrganizer).applyTransaction(capture(arg))
+ }
+ return arg.value
+ }
+
private fun getLatestExitDesktopWct(): WindowContainerTransaction {
val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
if (ENABLE_SHELL_TRANSITIONS) {
@@ -1429,6 +1815,10 @@
return arg.value
}
+ private fun findBoundsChange(wct: WindowContainerTransaction, task: RunningTaskInfo): Rect? =
+ wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds
+
+
private fun verifyWCTNotExecuted() {
if (ENABLE_SHELL_TRANSITIONS) {
verify(transitions, never()).startTransition(anyInt(), any(), isNull())
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 461b6b3..70ce202 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -2949,9 +2949,6 @@
dumpSetting(s, p,
Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ,
SystemSettingsProto.Screen.AUTO_BRIGHTNESS_ADJ);
- dumpSetting(s, p,
- Settings.System.SCREEN_BRIGHTNESS_FLOAT,
- SystemSettingsProto.Screen.BRIGHTNESS_FLOAT);
p.end(screenToken);
dumpSetting(s, p,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c891dfc..92167ee 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -935,7 +935,6 @@
Settings.System.VOLUME_VOICE, // deprecated since API 2?
Settings.System.WHEN_TO_MAKE_WIFI_CALLS, // bug?
Settings.System.WINDOW_ORIENTATION_LISTENER_LOG, // used for debugging only
- Settings.System.SCREEN_BRIGHTNESS_FLOAT,
Settings.System.SCREEN_BRIGHTNESS_FOR_ALS,
Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED_DURING_OOBE,
Settings.System.WEAR_TTS_PREWARM_ENABLED,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 21881f6..80398cd 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -550,6 +550,13 @@
}
flag {
+ name: "enable_contextual_tip_for_mute_volume"
+ namespace: "systemui"
+ description: "Enables the contextual tip for muting the volume."
+ bug: "337737048"
+}
+
+flag {
name: "disable_contextual_tips_frequency_check"
description: "Disables frequency capping check for contextual tips."
namespace: "systemui"
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
index 1e60b98..d4660fa 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt
@@ -44,6 +44,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.Flags.activityTransitionUseLargestWindow
+import java.util.concurrent.Executor
import kotlin.math.roundToInt
private const val TAG = "ActivityTransitionAnimator"
@@ -52,14 +53,19 @@
* A class that allows activities to be started in a seamless way from a view that is transforming
* nicely into the starting window.
*/
-class ActivityTransitionAnimator(
+class ActivityTransitionAnimator
+@JvmOverloads
+constructor(
+ /** The executor that runs on the main thread. */
+ private val mainExecutor: Executor,
+
/** The animator used when animating a View into an app. */
- private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
+ private val transitionAnimator: TransitionAnimator = defaultTransitionAnimator(mainExecutor),
/** The animator used when animating a Dialog into an app. */
// TODO(b/218989950): Remove this animator and instead set the duration of the dim fade out to
// TIMINGS.contentBeforeFadeOutDuration.
- private val dialogToAppAnimator: TransitionAnimator = DEFAULT_DIALOG_TO_APP_ANIMATOR,
+ private val dialogToAppAnimator: TransitionAnimator = defaultDialogToAppAnimator(mainExecutor),
/**
* Whether we should disable the WindowManager timeout. This should be set to true in tests
@@ -100,10 +106,6 @@
// TODO(b/288507023): Remove this flag.
@JvmField val DEBUG_TRANSITION_ANIMATION = Build.IS_DEBUGGABLE
- private val DEFAULT_TRANSITION_ANIMATOR = TransitionAnimator(TIMINGS, INTERPOLATORS)
- private val DEFAULT_DIALOG_TO_APP_ANIMATOR =
- TransitionAnimator(DIALOG_TIMINGS, INTERPOLATORS)
-
/** Durations & interpolators for the navigation bar fading in & out. */
private const val ANIMATION_DURATION_NAV_FADE_IN = 266L
private const val ANIMATION_DURATION_NAV_FADE_OUT = 133L
@@ -121,6 +123,14 @@
* cancelled by WM.
*/
private const val LONG_TRANSITION_TIMEOUT = 5_000L
+
+ private fun defaultTransitionAnimator(mainExecutor: Executor): TransitionAnimator {
+ return TransitionAnimator(mainExecutor, TIMINGS, INTERPOLATORS)
+ }
+
+ private fun defaultDialogToAppAnimator(mainExecutor: Executor): TransitionAnimator {
+ return TransitionAnimator(mainExecutor, DIALOG_TIMINGS, INTERPOLATORS)
+ }
}
/**
@@ -257,9 +267,7 @@
private fun Controller.callOnIntentStartedOnMainThread(willAnimate: Boolean) {
if (Looper.myLooper() != Looper.getMainLooper()) {
- this.transitionContainer.context.mainExecutor.execute {
- callOnIntentStartedOnMainThread(willAnimate)
- }
+ mainExecutor.execute { callOnIntentStartedOnMainThread(willAnimate) }
} else {
if (DEBUG_TRANSITION_ANIMATION) {
Log.d(
@@ -479,12 +487,10 @@
controller: Controller,
callback: Callback,
/** The animator to use to animate the window transition. */
- transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
+ transitionAnimator: TransitionAnimator,
/** Listener for animation lifecycle events. */
listener: Listener? = null
) : IRemoteAnimationRunner.Stub() {
- private val context = controller.transitionContainer.context
-
// This is being passed across IPC boundaries and cycles (through PendingIntentRecords,
// etc.) are possible. So we need to make sure we drop any references that might
// transitively cause leaks when we're done with animation.
@@ -493,11 +499,12 @@
init {
delegate =
AnimationDelegate(
+ mainExecutor,
controller,
callback,
DelegatingAnimationCompletionListener(listener, this::dispose),
transitionAnimator,
- disableWmTimeout
+ disableWmTimeout,
)
}
@@ -510,7 +517,7 @@
finishedCallback: IRemoteAnimationFinishedCallback?
) {
val delegate = delegate
- context.mainExecutor.execute {
+ mainExecutor.execute {
if (delegate == null) {
Log.i(TAG, "onAnimationStart called after completion")
// Animation started too late and timed out already. We need to still
@@ -525,7 +532,7 @@
@BinderThread
override fun onAnimationCancelled() {
val delegate = delegate
- context.mainExecutor.execute {
+ mainExecutor.execute {
delegate ?: Log.wtf(TAG, "onAnimationCancelled called after completion")
delegate?.onAnimationCancelled()
}
@@ -535,19 +542,21 @@
fun dispose() {
// Drop references to animation controller once we're done with the animation
// to avoid leaking.
- context.mainExecutor.execute { delegate = null }
+ mainExecutor.execute { delegate = null }
}
}
class AnimationDelegate
@JvmOverloads
constructor(
+ private val mainExecutor: Executor,
private val controller: Controller,
private val callback: Callback,
/** Listener for animation lifecycle events. */
private val listener: Listener? = null,
/** The animator to use to animate the window transition. */
- private val transitionAnimator: TransitionAnimator = DEFAULT_TRANSITION_ANIMATOR,
+ private val transitionAnimator: TransitionAnimator =
+ defaultTransitionAnimator(mainExecutor),
/**
* Whether we should disable the WindowManager timeout. This should be set to true in tests
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index b89ebfc..f5d01d7 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -37,6 +37,7 @@
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.util.maybeForceFullscreen
import com.android.systemui.util.registerAnimationOnBackInvoked
+import java.util.concurrent.Executor
import kotlin.math.roundToInt
private const val TAG = "DialogTransitionAnimator"
@@ -55,10 +56,16 @@
class DialogTransitionAnimator
@JvmOverloads
constructor(
+ private val mainExecutor: Executor,
private val callback: Callback,
private val interactionJankMonitor: InteractionJankMonitor,
private val featureFlags: AnimationFeatureFlags,
- private val transitionAnimator: TransitionAnimator = TransitionAnimator(TIMINGS, INTERPOLATORS),
+ private val transitionAnimator: TransitionAnimator =
+ TransitionAnimator(
+ mainExecutor,
+ TIMINGS,
+ INTERPOLATORS,
+ ),
private val isForTesting: Boolean = false,
) {
private companion object {
@@ -937,24 +944,9 @@
}
override fun onTransitionAnimationEnd(isExpandingFullyAbove: Boolean) {
- // onLaunchAnimationEnd is called by an Animator at the end of the animation,
- // on a Choreographer animation tick. The following calls will move the animated
- // content from the dialog overlay back to its original position, and this
- // change must be reflected in the next frame given that we then sync the next
- // frame of both the content and dialog ViewRoots. However, in case that content
- // is rendered by Compose, whose compositions are also scheduled on a
- // Choreographer frame, any state change made *right now* won't be reflected in
- // the next frame given that a Choreographer frame can't schedule another and
- // have it happen in the same frame. So we post the forwarded calls to
- // [Controller.onLaunchAnimationEnd], leaving this Choreographer frame, ensuring
- // that the move of the content back to its original window will be reflected in
- // the next frame right after [onLaunchAnimationEnd] is called.
- dialog.context.mainExecutor.execute {
- startController.onTransitionAnimationEnd(isExpandingFullyAbove)
- endController.onTransitionAnimationEnd(isExpandingFullyAbove)
-
- onLaunchAnimationEnd()
- }
+ startController.onTransitionAnimationEnd(isExpandingFullyAbove)
+ endController.onTransitionAnimationEnd(isExpandingFullyAbove)
+ onLaunchAnimationEnd()
}
override fun onTransitionAnimationProgress(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
index 679c969..cc55df1 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TransitionAnimator.kt
@@ -31,12 +31,17 @@
import androidx.annotation.VisibleForTesting
import com.android.app.animation.Interpolators.LINEAR
import com.android.systemui.shared.Flags.returnAnimationFrameworkLibrary
+import java.util.concurrent.Executor
import kotlin.math.roundToInt
private const val TAG = "TransitionAnimator"
/** A base class to animate a window (activity or dialog) launch to or return from a view . */
-class TransitionAnimator(private val timings: Timings, private val interpolators: Interpolators) {
+class TransitionAnimator(
+ private val mainExecutor: Executor,
+ private val timings: Timings,
+ private val interpolators: Interpolators,
+) {
companion object {
internal const val DEBUG = false
private val SRC_MODE = PorterDuffXfermode(PorterDuff.Mode.SRC)
@@ -351,11 +356,27 @@
if (DEBUG) {
Log.d(TAG, "Animation ended")
}
- controller.onTransitionAnimationEnd(isExpandingFullyAbove)
- transitionContainerOverlay.remove(windowBackgroundLayer)
- if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
- openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+ // onAnimationEnd is called at the end of the animation, on a Choreographer
+ // animation tick. During dialog launches, the following calls will move the
+ // animated content from the dialog overlay back to its original position, and
+ // this change must be reflected in the next frame given that we then sync the
+ // next frame of both the content and dialog ViewRoots. During SysUI activity
+ // launches, we will instantly collapse the shade at the end of the transition.
+ // However, if those are rendered by Compose, whose compositions are also
+ // scheduled on a Choreographer frame, any state change made *right now* won't
+ // be reflected in the next frame given that a Choreographer frame can't
+ // schedule another and have it happen in the same frame. So we post the
+ // forwarded calls to [Controller.onLaunchAnimationEnd] in the main executor,
+ // leaving this Choreographer frame, ensuring that any state change applied by
+ // onTransitionAnimationEnd() will be reflected in the same frame.
+ mainExecutor.execute {
+ controller.onTransitionAnimationEnd(isExpandingFullyAbove)
+ transitionContainerOverlay.remove(windowBackgroundLayer)
+
+ if (moveBackgroundLayerWhenAppVisibilityChanges && controller.isLaunching) {
+ openingWindowSyncViewOverlay?.remove(windowBackgroundLayer)
+ }
}
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index d924d88..92d5c26 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -74,6 +74,16 @@
*/
val isUserInputOngoing: Flow<Boolean>,
) : ObservableTransitionState
+
+ fun isIdle(scene: SceneKey?): Boolean {
+ return this is Idle && (scene == null || this.currentScene == scene)
+ }
+
+ fun isTransitioning(from: SceneKey? = null, to: SceneKey? = null): Boolean {
+ return this is Transition &&
+ (from == null || this.fromScene == from) &&
+ (to == null || this.toScene == to)
+ }
}
/**
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index 9d6a66d..1f8cb8a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -90,7 +90,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class CommunalViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class CommunalViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var mediaHost: MediaHost
@Mock private lateinit var user: UserInfo
@Mock private lateinit var providerInfo: AppWidgetProviderInfo
@@ -111,7 +111,7 @@
private lateinit var underTest: CommunalViewModel
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
index 3b6f6a2..f31eb7f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -58,7 +58,7 @@
@ExperimentalCoroutinesApi
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class UdfpsKeyguardInteractorTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class UdfpsKeyguardInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
val keyguardRepository = kosmos.fakeKeyguardRepository
@@ -88,7 +88,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
index f52c66e..cde703b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModelTest.kt
@@ -43,7 +43,7 @@
@ExperimentalCoroutinesApi
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class AodToLockscreenTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class AodToLockscreenTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
val repository = kosmos.fakeKeyguardTransitionRepository
@@ -60,7 +60,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
index fee18dd..d632936 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/BouncerToGoneFlowsTest.kt
@@ -16,13 +16,15 @@
package com.android.systemui.keyguard.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.mockPrimaryBouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
@@ -31,29 +33,25 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.shadeRepository
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlin.time.Duration.Companion.milliseconds
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mock
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class BouncerToGoneFlowsTest : SysuiTestCase() {
- @Mock private lateinit var shadeInteractor: ShadeInteractor
-
- private val shadeExpansionStateFlow = MutableStateFlow(0.1f)
+@RunWith(ParameterizedAndroidJunit4::class)
+class BouncerToGoneFlowsTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
@@ -61,16 +59,31 @@
}
private val testScope = kosmos.testScope
private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
- private val shadeRepository = kosmos.shadeRepository
private val sysuiStatusBarStateController = kosmos.sysuiStatusBarStateController
private val primaryBouncerInteractor = kosmos.mockPrimaryBouncerInteractor
- private val underTest = kosmos.bouncerToGoneFlows
+
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+ private lateinit var underTest: BouncerToGoneFlows
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(false)
sysuiStatusBarStateController.setLeaveOpenOnKeyguardHide(false)
+ underTest = kosmos.bouncerToGoneFlows
}
@Test
@@ -79,7 +92,7 @@
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
runCurrent()
- shadeRepository.setLockscreenShadeExpansion(1f)
+ shadeTestUtil.setLockscreenShadeExpansion(1f)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
keyguardTransitionRepository.sendTransitionSteps(
@@ -99,12 +112,13 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun scrimAlpha_runDimissFromKeyguard_shadeNotExpanded() =
testScope.runTest {
val values by collectValues(underTest.scrimAlpha(500.milliseconds, PRIMARY_BOUNCER))
runCurrent()
- shadeRepository.setLockscreenShadeExpansion(0f)
+ shadeTestUtil.setLockscreenShadeExpansion(0f)
whenever(primaryBouncerInteractor.willRunDismissFromKeyguard()).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
index 2e1765a..838b2a7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModelTest.kt
@@ -60,7 +60,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardRootViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardRootViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
@@ -75,7 +75,6 @@
private val viewState = ViewStateAccessor()
- // add to init block
companion object {
@JvmStatic
@Parameters(name = "{0}")
@@ -85,7 +84,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index ec2cb04..de4b999 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -47,7 +47,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenContentViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenContentViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos: Kosmos = testKosmos()
@@ -62,7 +62,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
index e3ae3ba..bc381f2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToAodTransitionViewModelTest.kt
@@ -49,7 +49,7 @@
@ExperimentalCoroutinesApi
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToAodTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenToAodTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
@@ -73,7 +73,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index adeb395..9337793 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -51,7 +51,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToDreamingTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenToDreamingTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
@@ -73,7 +73,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index f8da74f..6ce7e88 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -54,7 +54,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToOccludedTransitionViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class LockscreenToOccludedTransitionViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
@@ -76,7 +76,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index d5df159..58c6817 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -46,7 +46,7 @@
@ExperimentalCoroutinesApi
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameterization?) :
+class LockscreenToPrimaryBouncerTransitionViewModelTest(flags: FlagsParameterization) :
SysuiTestCase() {
private val kosmos =
testKosmos().apply {
@@ -67,7 +67,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
index aa0ca18..78c4def 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/ShadeInteractorImplTest.kt
@@ -60,7 +60,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class ShadeInteractorImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class ShadeInteractorImplTest(flags: FlagsParameterization) : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
val configurationRepository by lazy { kosmos.fakeConfigurationRepository }
@@ -85,7 +85,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
index 44c9695..cecc70c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt
@@ -60,7 +60,7 @@
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class ShadeStartableTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val shadeInteractor by lazy { kosmos.shadeInteractor }
@@ -80,7 +80,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index d353a62..21dc953 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -65,7 +65,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper
-class StatusBarStateControllerImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class StatusBarStateControllerImplTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
@@ -84,7 +84,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index cbbc4d8..9367a93 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -59,7 +59,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterization?) :
+class NotificationIconContainerStatusBarViewModelTest(flags: FlagsParameterization) :
SysuiTestCase() {
companion object {
@@ -71,7 +71,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
private val kosmos = testKosmos()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 7ac549a..cc5df74 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -20,12 +20,13 @@
import android.app.NotificationManager.Policy
import android.platform.test.annotations.EnableFlags
+import android.platform.test.flag.junit.FlagsParameterization
import android.provider.Settings
-import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -33,7 +34,7 @@
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -56,11 +57,13 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@SmallTest
-@RunWith(AndroidJUnit4::class)
+@RunWith(ParameterizedAndroidJunit4::class)
@EnableFlags(FooterViewRefactor.FLAG_NAME)
-class NotificationListViewModelTest : SysuiTestCase() {
+class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
@@ -72,16 +75,30 @@
private val fakeKeyguardRepository = kosmos.fakeKeyguardRepository
private val fakePowerRepository = kosmos.fakePowerRepository
private val fakeRemoteInputRepository = kosmos.fakeRemoteInputRepository
- private val fakeShadeRepository = kosmos.fakeShadeRepository
private val fakeUserSetupRepository = kosmos.fakeUserSetupRepository
private val headsUpRepository = kosmos.headsUpNotificationRepository
private val zenModeRepository = kosmos.zenModeRepository
- val underTest = kosmos.notificationListViewModel
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+ private lateinit var underTest: NotificationListViewModel
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ underTest = kosmos.notificationListViewModel
}
@Test
@@ -163,7 +180,7 @@
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
// AND quick settings are expanded
- fakeShadeRepository.legacyQsFullscreen.value = true
+ shadeTestUtil.setQsFullscreen(true)
runCurrent()
// THEN empty shade is not visible
@@ -178,9 +195,10 @@
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
// AND quick settings are expanded
- fakeShadeRepository.setQsExpansion(1f)
- // AND split shade is enabled
+ shadeTestUtil.setQsExpansion(1f)
+ // AND split shade is expanded
overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeTestUtil.setShadeExpansion(1f)
fakeConfigurationController.notifyConfigurationChanged()
runCurrent()
@@ -290,7 +308,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
runCurrent()
// THEN footer is visible
@@ -306,7 +324,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open on lockscreen
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE_LOCKED)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
runCurrent()
// THEN footer is visible
@@ -337,7 +355,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
// AND user is not set up
fakeUserSetupRepository.setUserSetUp(false)
runCurrent()
@@ -355,7 +373,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
// AND device is starting to go to sleep
fakePowerRepository.updateWakefulness(WakefulnessState.STARTING_TO_SLEEP)
runCurrent()
@@ -373,10 +391,10 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
// AND quick settings are expanded
- fakeShadeRepository.setQsExpansion(1f)
- fakeShadeRepository.legacyQsFullscreen.value = true
+ shadeTestUtil.setQsExpansion(1f)
+ shadeTestUtil.setQsFullscreen(true)
runCurrent()
// THEN footer is not visible
@@ -390,11 +408,11 @@
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
+ // AND quick settings are expanded
+ shadeTestUtil.setQsExpansion(1f)
// AND shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
- // AND quick settings are expanded
- fakeShadeRepository.setQsExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
// AND split shade is enabled
overrideResource(R.bool.config_use_split_notification_shade, true)
fakeConfigurationController.notifyConfigurationChanged()
@@ -413,7 +431,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
// AND remote input is active
fakeRemoteInputRepository.isRemoteInputActive.value = true
runCurrent()
@@ -431,7 +449,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND shade is open and fully expanded
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
runCurrent()
// THEN footer visibility animates
@@ -447,7 +465,7 @@
activeNotificationListRepository.setActiveNotifs(count = 2)
// AND we are on the keyguard
fakeKeyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
runCurrent()
// THEN footer visibility does not animate
@@ -461,7 +479,7 @@
// WHEN shade is closed
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(0f)
+ shadeTestUtil.setShadeExpansion(0f)
runCurrent()
// THEN footer is hidden
@@ -475,7 +493,7 @@
// WHEN shade is open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setShadeExpansion(1f)
runCurrent()
// THEN footer is hidden
@@ -489,8 +507,8 @@
// WHEN QS partially open
fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
- fakeShadeRepository.setQsExpansion(0.5f)
- fakeShadeRepository.setLegacyShadeExpansion(0.5f)
+ shadeTestUtil.setQsExpansion(0.5f)
+ shadeTestUtil.setShadeExpansion(0.5f)
runCurrent()
// THEN footer is hidden
@@ -588,7 +606,7 @@
testScope.runTest {
val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
- fakeShadeRepository.setQsExpansion(0.0f)
+ shadeTestUtil.setQsExpansion(0.0f)
fakeKeyguardRepository.setKeyguardShowing(false)
runCurrent()
@@ -601,7 +619,7 @@
testScope.runTest {
val animationsEnabled by collectLastValue(underTest.headsUpAnimationsEnabled)
- fakeShadeRepository.setQsExpansion(0.0f)
+ shadeTestUtil.setQsExpansion(0.0f)
fakeKeyguardRepository.setKeyguardShowing(true)
runCurrent()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2cd295c..f2ce745 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -75,7 +75,7 @@
@RunWith(ParameterizedAndroidJunit4::class)
// SharedNotificationContainerViewModel is only bound when FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT is on
@EnableFlags(FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
-class SharedNotificationContainerViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
companion object {
@JvmStatic
@@ -89,7 +89,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
val aodBurnInViewModel = mock(AodBurnInViewModel::class.java)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
index 8ce5037..63f19fb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/AvalancheControllerTest.kt
@@ -249,22 +249,40 @@
@Test
- fun testDelete_showingEntryKeyBecomesPreviousHunKey() {
+ fun testDelete_deleteSecondToLastEntry_showingEntryKeyBecomesPreviousHunKey() {
mAvalancheController.previousHunKey = ""
// Entry is showing
+ val firstEntry = createHeadsUpEntry(id = 0)
+ mAvalancheController.headsUpEntryShowing = firstEntry
+
+ // There's another entry waiting to show next
+ val secondEntry = createHeadsUpEntry(id = 1)
+ mAvalancheController.addToNext(secondEntry, runnableMock!!)
+
+ // Delete
+ mAvalancheController.delete(firstEntry, runnableMock, "testLabel")
+
+ // Next entry is shown
+ assertThat(mAvalancheController.previousHunKey).isEqualTo(firstEntry.mEntry!!.key)
+ }
+
+ @Test
+ fun testDelete_deleteLastEntry_previousHunKeyCleared() {
+ mAvalancheController.previousHunKey = "key"
+
+ // Nothing waiting to show
+ mAvalancheController.clearNext()
+
+ // One entry is showing
val showingEntry = createHeadsUpEntry(id = 0)
mAvalancheController.headsUpEntryShowing = showingEntry
- // There's another entry waiting to show next
- val nextEntry = createHeadsUpEntry(id = 1)
- mAvalancheController.addToNext(nextEntry, runnableMock!!)
-
// Delete
- mAvalancheController.delete(showingEntry, runnableMock, "testLabel")
+ mAvalancheController.delete(showingEntry, runnableMock!!, "testLabel")
// Next entry is shown
- assertThat(mAvalancheController.previousHunKey).isEqualTo(showingEntry.mEntry!!.key)
+ assertThat(mAvalancheController.previousHunKey).isEqualTo("");
}
@Test
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
index ae9f57f..6032f0b 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/data/repository/AccessibilityRepository.kt
@@ -18,7 +18,7 @@
import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
-import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.Module
import dagger.Provides
import kotlinx.coroutines.channels.awaitClose
@@ -29,6 +29,8 @@
interface AccessibilityRepository {
/** @see [AccessibilityManager.isTouchExplorationEnabled] */
val isTouchExplorationEnabled: Flow<Boolean>
+ /** @see [AccessibilityManager.isEnabled] */
+ val isEnabled: Flow<Boolean>
companion object {
operator fun invoke(a11yManager: AccessibilityManager): AccessibilityRepository =
@@ -47,6 +49,15 @@
awaitClose { manager.removeTouchExplorationStateChangeListener(listener) }
}
.distinctUntilChanged()
+
+ override val isEnabled: Flow<Boolean> =
+ conflatedCallbackFlow {
+ val listener = AccessibilityManager.AccessibilityStateChangeListener(::trySend)
+ manager.addAccessibilityStateChangeListener(listener)
+ trySend(manager.isEnabled)
+ awaitClose { manager.removeAccessibilityStateChangeListener(listener) }
+ }
+ .distinctUntilChanged()
}
@Module
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
index 968ce0d..93b624a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/domain/interactor/AccessibilityInteractor.kt
@@ -28,6 +28,8 @@
private val a11yRepo: AccessibilityRepository,
) {
/** @see [android.view.accessibility.AccessibilityManager.isTouchExplorationEnabled] */
- val isTouchExplorationEnabled: Flow<Boolean>
- get() = a11yRepo.isTouchExplorationEnabled
+ val isTouchExplorationEnabled: Flow<Boolean> = a11yRepo.isTouchExplorationEnabled
+
+ /** @see [android.view.accessibility.AccessibilityManager.isEnabled] */
+ val isEnabled: Flow<Boolean> = a11yRepo.isEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
index 5a28f711..9b07675f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.wm.shell.animation.Interpolators
import javax.inject.Inject
@@ -140,6 +141,8 @@
}
private fun listenForAlternateBouncerToGone() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
if (KeyguardWmStateRefactor.isEnabled) {
// Handled via #dismissAlternateBouncer.
return
@@ -162,6 +165,8 @@
}
private fun listenForAlternateBouncerToPrimaryBouncer() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.primaryBouncerShowing
.filterRelevantKeyguardStateAnd { isPrimaryBouncerShowing ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
index 4d73774..a306954 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -185,6 +186,7 @@
* PRIMARY_BOUNCER.
*/
private fun listenForAodToPrimaryBouncer() {
+ if (SceneContainerFlag.isEnabled) return
scope.launch("$TAG#listenForAodToPrimaryBouncer") {
keyguardInteractor.primaryBouncerShowing
.filterRelevantKeyguardStateAnd { primaryBouncerShowing -> primaryBouncerShowing }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
index e738ea4..63294f7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingLockscreenHostedTransitionInteractor.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -93,6 +94,8 @@
}
private fun listenForDreamingLockscreenHostedToPrimaryBouncer() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.primaryBouncerShowing
.filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -101,6 +104,8 @@
}
private fun listenForDreamingLockscreenHostedToGone() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.biometricUnlockState
.filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
index c952e08..7961b45 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromDreamingTransitionInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -88,6 +89,8 @@
private fun listenForDreamingToGlanceableHub() {
if (!communalHub()) return
+ if (SceneContainerFlag.isEnabled)
+ return // TODO(b/336576536): Check if adaptation for scene framework is needed
scope.launch("$TAG#listenForDreamingToGlanceableHub", mainDispatcher) {
glanceableHubTransitions.listenForGlanceableHubTransition(
transitionOwnerName = TAG,
@@ -175,6 +178,8 @@
}
private fun listenForDreamingToGoneWhenDismissable() {
+ if (SceneContainerFlag.isEnabled)
+ return // TODO(b/336576536): Check if adaptation for scene framework is needed
scope.launch {
keyguardInteractor.isAbleToDream
.sampleCombine(
@@ -190,6 +195,8 @@
}
private fun listenForDreamingToGoneFromBiometricUnlock() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.biometricUnlockState
.filterRelevantKeyguardStateAnd { biometricUnlockState ->
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
index faab033..da4e989d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGlanceableHubTransitionInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import javax.inject.Inject
@@ -62,6 +63,8 @@
) {
override fun start() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
if (!Flags.communalHub()) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
index c2c095b..2b3732f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromGoneTransitionInteractor.kt
@@ -29,6 +29,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
@@ -62,6 +63,8 @@
) {
override fun start() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
listenForGoneToAodOrDozing()
listenForGoneToDreaming()
listenForGoneToLockscreenOrHub()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 56261e0..dad2d96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -20,6 +20,7 @@
import android.util.MathUtils
import com.android.app.animation.Interpolators
import com.android.app.tracing.coroutines.launch
+import com.android.systemui.Flags
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -32,6 +33,7 @@
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine
import java.util.UUID
@@ -150,6 +152,7 @@
}
private fun listenForLockscreenToPrimaryBouncer() {
+ if (SceneContainerFlag.isEnabled) return
scope.launch("$TAG#listenForLockscreenToPrimaryBouncer") {
keyguardInteractor.primaryBouncerShowing
.filterRelevantKeyguardStateAnd { isBouncerShowing -> isBouncerShowing }
@@ -174,6 +177,7 @@
/* Starts transitions when manually dragging up the bouncer from the lockscreen. */
private fun listenForLockscreenToPrimaryBouncerDragging() {
+ if (SceneContainerFlag.isEnabled) return
var transitionId: UUID? = null
scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") {
shadeRepository.legacyShadeExpansion
@@ -280,6 +284,7 @@
}
private fun listenForLockscreenToGoneDragging() {
+ if (SceneContainerFlag.isEnabled) return
if (KeyguardWmStateRefactor.isEnabled) {
// When the refactor is enabled, we no longer use isKeyguardGoingAway.
scope.launch("$TAG#listenForLockscreenToGoneDragging") {
@@ -337,7 +342,9 @@
* keyguard transition.
*/
private fun listenForLockscreenToGlanceableHub() {
- if (!com.android.systemui.Flags.communalHub()) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
+ if (!Flags.communalHub()) {
return
}
scope.launch(mainDispatcher) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index e51ba83..9559250 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -26,6 +26,7 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
@@ -89,18 +90,10 @@
.filterRelevantKeyguardStateAnd { onTop -> !onTop }
.sample(
communalInteractor.isIdleOnCommunal,
- communalInteractor.showCommunalFromOccluded,
+ communalInteractor.showCommunalFromOccluded
)
.collect { (_, isIdleOnCommunal, showCommunalFromOccluded) ->
- // Occlusion signals come from the framework, and should interrupt any
- // existing transition
- val to =
- if (isIdleOnCommunal || showCommunalFromOccluded) {
- KeyguardState.GLANCEABLE_HUB
- } else {
- KeyguardState.LOCKSCREEN
- }
- startTransitionTo(to)
+ startTransitionToLockscreenOrHub(isIdleOnCommunal, showCommunalFromOccluded)
}
}
} else {
@@ -115,21 +108,28 @@
!isOccluded && isShowing
}
.collect { (_, _, isIdleOnCommunal, showCommunalFromOccluded) ->
- // Occlusion signals come from the framework, and should interrupt any
- // existing transition
- val to =
- if (isIdleOnCommunal || showCommunalFromOccluded) {
- KeyguardState.GLANCEABLE_HUB
- } else {
- KeyguardState.LOCKSCREEN
- }
- startTransitionTo(to)
+ startTransitionToLockscreenOrHub(isIdleOnCommunal, showCommunalFromOccluded)
}
}
}
}
+ private suspend fun FromOccludedTransitionInteractor.startTransitionToLockscreenOrHub(
+ isIdleOnCommunal: Boolean,
+ showCommunalFromOccluded: Boolean,
+ ) {
+ if (isIdleOnCommunal || showCommunalFromOccluded) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
+ startTransitionTo(KeyguardState.GLANCEABLE_HUB)
+ } else {
+ startTransitionTo(KeyguardState.LOCKSCREEN)
+ }
+ }
+
private fun listenForOccludedToGone() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
if (KeyguardWmStateRefactor.isEnabled) {
// We don't think OCCLUDED to GONE is possible. You should always have to go via a
// *_BOUNCER state to end up GONE. Launching an activity over a dismissable keyguard
@@ -150,10 +150,6 @@
}
}
- fun dismissToGone() {
- scope.launch { startTransitionTo(KeyguardState.GONE) }
- }
-
private fun listenForOccludedToAsleep() {
scope.launch { listenForSleepTransition() }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
index 181a551..53a0c32 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromPrimaryBouncerTransitionInteractor.kt
@@ -28,6 +28,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
import com.android.systemui.power.domain.interactor.PowerInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.sample
import com.android.systemui.util.kotlin.sample
@@ -98,6 +99,8 @@
}
private fun listenForPrimaryBouncerToLockscreenHubOrOccluded() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
if (KeyguardWmStateRefactor.isEnabled) {
scope.launch {
keyguardInteractor.primaryBouncerShowing
@@ -158,10 +161,14 @@
}
private fun listenForPrimaryBouncerToAsleep() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch { listenForSleepTransition() }
}
private fun listenForPrimaryBouncerToDreamingLockscreenHosted() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
scope.launch {
keyguardInteractor.primaryBouncerShowing
.sample(keyguardInteractor.isActiveDreamLockscreenHosted, ::Pair)
@@ -174,6 +181,8 @@
}
private fun listenForPrimaryBouncerToGone() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
if (KeyguardWmStateRefactor.isEnabled) {
// This is handled in KeyguardSecurityContainerController and
// StatusBarKeyguardViewManager, which calls the transition interactor to kick off a
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
index 197221a..fcf67d5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GlanceableHubTransitions.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.sample
import java.util.UUID
import javax.inject.Inject
@@ -49,6 +50,8 @@
fromState: KeyguardState,
toState: KeyguardState,
) {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
val toScene =
if (fromState == KeyguardState.GLANCEABLE_HUB) {
CommunalScenes.Blank
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index a18579d..2c05d49 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -28,11 +28,11 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
-import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.kotlin.pairwise
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -356,6 +356,8 @@
* state.
*/
fun startDismissKeyguardTransition() {
+ // TODO(b/336576536): Check if adaptation for scene framework is needed
+ if (SceneContainerFlag.isEnabled) return
when (val startedState = startedKeyguardState.replayCache.last()) {
LOCKSCREEN -> fromLockscreenTransitionInteractor.get().dismissKeyguard()
PRIMARY_BOUNCER -> fromPrimaryBouncerTransitionInteractor.get().dismissPrimaryBouncer()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
index bb2eeb7..dc35e43 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt
@@ -16,11 +16,16 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor
+import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -42,6 +47,7 @@
fromBouncerInteractor: FromPrimaryBouncerTransitionInteractor,
fromAlternateBouncerInteractor: FromAlternateBouncerTransitionInteractor,
notificationLaunchAnimationInteractor: NotificationLaunchAnimationInteractor,
+ sceneInteractor: SceneInteractor,
) {
private val defaultSurfaceBehindVisibility =
transitionInteractor.finishedKeyguardState.map(::isSurfaceVisible)
@@ -103,21 +109,42 @@
* animation. This is used to keep the RemoteAnimationTarget alive until we're done using it.
*/
val usingKeyguardGoingAwayAnimation: Flow<Boolean> =
- combine(
- transitionInteractor.isInTransitionToState(KeyguardState.GONE),
- transitionInteractor.finishedKeyguardState,
- surfaceBehindInteractor.isAnimatingSurface,
- notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
- ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
- // Using the animation if we're animating it directly, or if the
- // ActivityLaunchAnimator is in the process of animating it.
- val animationsRunning = isAnimatingSurface || notifLaunchRunning
- // We may still be animating the surface after the keyguard is fully GONE, since
- // some animations (like the translation spring) are not tied directly to the
- // transition step amount.
- isInTransitionToGone || (finishedState == KeyguardState.GONE && animationsRunning)
- }
- .distinctUntilChanged()
+ if (SceneContainerFlag.isEnabled) {
+ combine(
+ sceneInteractor.transitionState,
+ surfaceBehindInteractor.isAnimatingSurface,
+ notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+ ) { transition, isAnimatingSurface, isLaunchAnimationRunning ->
+ // Using the animation if we're animating it directly, or if the
+ // ActivityLaunchAnimator is in the process of animating it.
+ val isAnyAnimationRunning = isAnimatingSurface || isLaunchAnimationRunning
+ // We may still be animating the surface after the keyguard is fully GONE, since
+ // some animations (like the translation spring) are not tied directly to the
+ // transition step amount.
+ transition.isTransitioning(to = Scenes.Gone) ||
+ (isAnyAnimationRunning &&
+ (transition.isIdle(Scenes.Gone) ||
+ transition.isTransitioning(from = Scenes.Gone)))
+ }
+ .distinctUntilChanged()
+ } else {
+ combine(
+ transitionInteractor.isInTransitionToState(KeyguardState.GONE),
+ transitionInteractor.finishedKeyguardState,
+ surfaceBehindInteractor.isAnimatingSurface,
+ notificationLaunchAnimationInteractor.isLaunchAnimationRunning,
+ ) { isInTransitionToGone, finishedState, isAnimatingSurface, notifLaunchRunning ->
+ // Using the animation if we're animating it directly, or if the
+ // ActivityLaunchAnimator is in the process of animating it.
+ val animationsRunning = isAnimatingSurface || notifLaunchRunning
+ // We may still be animating the surface after the keyguard is fully GONE, since
+ // some animations (like the translation spring) are not tied directly to the
+ // transition step amount.
+ isInTransitionToGone ||
+ (finishedState == KeyguardState.GONE && animationsRunning)
+ }
+ .distinctUntilChanged()
+ }
/**
* Whether the lockscreen is visible, from the Window Manager (WM) perspective.
@@ -127,28 +154,44 @@
* want to know if the AOD/clock/notifs/etc. are visible.
*/
val lockscreenVisibility: Flow<Boolean> =
- transitionInteractor.currentKeyguardState
- .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
- .map { (currentState, startedWithPrev) ->
- val startedFromStep = startedWithPrev?.previousValue
- val startedStep = startedWithPrev?.newValue
- val returningToGoneAfterCancellation =
- startedStep?.to == KeyguardState.GONE &&
- startedFromStep?.transitionState == TransitionState.CANCELED &&
- startedFromStep.from == KeyguardState.GONE
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.transitionState
+ .pairwise(ObservableTransitionState.Idle(Scenes.Lockscreen))
+ .map { (prevTransitionState, transitionState) ->
+ val isReturningToGoneAfterCancellation =
+ prevTransitionState.isTransitioning(from = Scenes.Gone) &&
+ transitionState.isTransitioning(to = Scenes.Gone)
+ val isNotOnGone =
+ !transitionState.isTransitioning(from = Scenes.Gone) &&
+ !transitionState.isIdle(Scenes.Gone)
- if (!returningToGoneAfterCancellation) {
- // By default, apply the lockscreen visibility of the current state.
- KeyguardState.lockscreenVisibleInState(currentState)
- } else {
- // If we're transitioning to GONE after a prior canceled transition from GONE,
- // then this is the camera launch transition from an asleep state back to GONE.
- // We don't want to show the lockscreen since we're aborting the lock and going
- // back to GONE.
- KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+ isNotOnGone && !isReturningToGoneAfterCancellation
}
- }
- .distinctUntilChanged()
+ .distinctUntilChanged()
+ } else {
+ transitionInteractor.currentKeyguardState
+ .sample(transitionInteractor.startedStepWithPrecedingStep, ::Pair)
+ .map { (currentState, startedWithPrev) ->
+ val startedFromStep = startedWithPrev?.previousValue
+ val startedStep = startedWithPrev?.newValue
+ val returningToGoneAfterCancellation =
+ startedStep?.to == KeyguardState.GONE &&
+ startedFromStep?.transitionState == TransitionState.CANCELED &&
+ startedFromStep.from == KeyguardState.GONE
+
+ if (!returningToGoneAfterCancellation) {
+ // By default, apply the lockscreen visibility of the current state.
+ KeyguardState.lockscreenVisibleInState(currentState)
+ } else {
+ // If we're transitioning to GONE after a prior canceled transition from
+ // GONE, then this is the camera launch transition from an asleep state back
+ // to GONE. We don't want to show the lockscreen since we're aborting the
+ // lock and going back to GONE.
+ KeyguardState.lockscreenVisibleInState(KeyguardState.GONE)
+ }
+ }
+ .distinctUntilChanged()
+ }
/**
* Whether always-on-display (AOD) is visible when the lockscreen is visible, from window
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 4f00495..e2b66c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -76,7 +76,7 @@
view,
HapticFeedbackConstants.CONFIRM,
)
- applicationScope.launch { viewModel.onLongPress() }
+ applicationScope.launch { viewModel.onUserInteraction() }
}
}
@@ -116,6 +116,17 @@
launch("$TAG#viewModel.accessibilityDelegateHint") {
viewModel.accessibilityDelegateHint.collect { hint ->
view.accessibilityHintType = hint
+ if (hint != DeviceEntryIconView.AccessibilityHintType.NONE) {
+ view.setOnClickListener {
+ vibratorHelper.performHapticFeedback(
+ view,
+ HapticFeedbackConstants.CONFIRM,
+ )
+ applicationScope.launch { viewModel.onUserInteraction() }
+ }
+ } else {
+ view.setOnClickListener(null)
+ }
}
}
launch("$TAG#viewModel.useBackgroundProtection") {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 35b2598..200d30c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -65,12 +65,12 @@
object : AccessibilityDelegate() {
private val accessibilityAuthenticateHint =
AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
resources.getString(R.string.accessibility_authenticate_hint)
)
private val accessibilityEnterHint =
AccessibilityNodeInfo.AccessibilityAction(
- AccessibilityNodeInfoCompat.ACTION_LONG_CLICK,
+ AccessibilityNodeInfoCompat.ACTION_CLICK,
resources.getString(R.string.accessibility_enter_hint)
)
override fun onInitializeAccessibilityNodeInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
index da2fcc4..53b2697 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt
@@ -19,6 +19,7 @@
import android.animation.FloatEvaluator
import android.animation.IntEvaluator
import com.android.keyguard.KeyguardViewController
+import com.android.systemui.accessibility.domain.interactor.AccessibilityInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -68,6 +69,7 @@
private val keyguardViewController: Lazy<KeyguardViewController>,
private val deviceEntryInteractor: DeviceEntryInteractor,
private val deviceEntrySourceInteractor: DeviceEntrySourceInteractor,
+ private val accessibilityInteractor: AccessibilityInteractor,
@Application private val scope: CoroutineScope,
) {
val isUdfpsSupported: StateFlow<Boolean> = deviceEntryUdfpsInteractor.isUdfpsSupported
@@ -232,7 +234,8 @@
}
}
val isVisible: Flow<Boolean> = deviceEntryViewAlpha.map { it > 0f }.distinctUntilChanged()
- val isLongPressEnabled: Flow<Boolean> =
+
+ private val isInteractive: Flow<Boolean> =
combine(
iconType,
isUdfpsSupported,
@@ -244,17 +247,24 @@
DeviceEntryIconView.IconType.NONE -> false
}
}
-
val accessibilityDelegateHint: Flow<DeviceEntryIconView.AccessibilityHintType> =
- combine(iconType, isLongPressEnabled) { deviceEntryStatus, longPressEnabled ->
- if (longPressEnabled) {
- deviceEntryStatus.toAccessibilityHintType()
+ accessibilityInteractor.isEnabled.flatMapLatest { touchExplorationEnabled ->
+ if (touchExplorationEnabled) {
+ combine(iconType, isInteractive) { iconType, isInteractive ->
+ if (isInteractive) {
+ iconType.toAccessibilityHintType()
+ } else {
+ DeviceEntryIconView.AccessibilityHintType.NONE
+ }
+ }
} else {
- DeviceEntryIconView.AccessibilityHintType.NONE
+ flowOf(DeviceEntryIconView.AccessibilityHintType.NONE)
}
}
- suspend fun onLongPress() {
+ val isLongPressEnabled: Flow<Boolean> = isInteractive
+
+ suspend fun onUserInteraction() {
if (SceneContainerFlag.isEnabled) {
deviceEntryInteractor.attemptDeviceEntry()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index c17da4b..0524589 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -32,6 +32,7 @@
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpHandler;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -56,10 +57,10 @@
import com.android.systemui.statusbar.phone.CentralSurfacesImpl;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.phone.ManagedProfileControllerImpl;
-import com.android.systemui.statusbar.phone.ui.StatusBarIconList;
import com.android.systemui.statusbar.phone.StatusBarRemoteInputCallback;
import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
import com.android.systemui.statusbar.phone.ui.StatusBarIconControllerImpl;
+import com.android.systemui.statusbar.phone.ui.StatusBarIconList;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import dagger.Binds;
@@ -209,14 +210,16 @@
/** */
@Provides
@SysUISingleton
- static ActivityTransitionAnimator provideActivityTransitionAnimator() {
- return new ActivityTransitionAnimator();
+ static ActivityTransitionAnimator provideActivityTransitionAnimator(
+ @Main Executor mainExecutor) {
+ return new ActivityTransitionAnimator(mainExecutor);
}
/** */
@Provides
@SysUISingleton
- static DialogTransitionAnimator provideDialogTransitionAnimator(IDreamManager dreamManager,
+ static DialogTransitionAnimator provideDialogTransitionAnimator(@Main Executor mainExecutor,
+ IDreamManager dreamManager,
KeyguardStateController keyguardStateController,
Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
InteractionJankMonitor interactionJankMonitor,
@@ -243,7 +246,7 @@
}
};
return new DialogTransitionAnimator(
- callback, interactionJankMonitor, animationFeatureFlags);
+ mainExecutor, callback, interactionJankMonitor, animationFeatureFlags);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
index d9d909a..fc54f14 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/SubscriptionModel.kt
@@ -33,6 +33,18 @@
*/
val isOpportunistic: Boolean = false,
+ /**
+ * True if this subscription **only** supports non-terrestrial networks (NTN) and false
+ * otherwise. (non-terrestrial == satellite)
+ *
+ * Note that we intend to filter these subscriptions out, because these connections are actually
+ * supported by
+ * [com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository]. See
+ * [com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor] for
+ * the filtering.
+ */
+ val isExclusivelyNonTerrestrial: Boolean = false,
+
/** Subscriptions in the same group may be filtered or treated as a single subscription */
val groupUuid: ParcelUuid? = null,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
index 2278597..425c58b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileConnectionRepository.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
+import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.flow.StateFlow
@@ -76,7 +77,17 @@
*/
val isInService: StateFlow<Boolean>
- /** Reflects [android.telephony.ServiceState.isUsingNonTerrestrialNetwork] */
+ /**
+ * True if this subscription is actively connected to a non-terrestrial network and false
+ * otherwise. Reflects [android.telephony.ServiceState.isUsingNonTerrestrialNetwork].
+ *
+ * Notably: This value reflects that this subscription is **currently** using a non-terrestrial
+ * network, because some subscriptions can switch between terrestrial and non-terrestrial
+ * networks. [SubscriptionModel.isExclusivelyNonTerrestrial] reflects whether a subscription is
+ * configured to exclusively connect to non-terrestrial networks. [isNonTerrestrial] can change
+ * during the lifetime of a subscription but [SubscriptionModel.isExclusivelyNonTerrestrial]
+ * will stay constant.
+ */
val isNonTerrestrial: StateFlow<Boolean>
/** True if [android.telephony.SignalStrength] told us that this connection is using GSM */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 5d91ef3..0073e9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -424,6 +424,7 @@
SubscriptionModel(
subscriptionId = subscriptionId,
isOpportunistic = isOpportunistic,
+ isExclusivelyNonTerrestrial = isOnlyNonTerrestrialNetwork,
groupUuid = groupUuid,
carrierName = carrierName.toString(),
profileClass = profileClass,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
index d555c47..91d7ca6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt
@@ -172,21 +172,33 @@
private val unfilteredSubscriptions: Flow<List<SubscriptionModel>> =
mobileConnectionsRepo.subscriptions
- /**
- * Any filtering that we can do based purely on the info of each subscription. Currently this
- * only applies the ProfileClass-based filter, but if we need other they can go here
- */
+ /** Any filtering that we can do based purely on the info of each subscription individually. */
private val subscriptionsBasedFilteredSubs =
- unfilteredSubscriptions.map { subs -> applyProvisioningFilter(subs) }.distinctUntilChanged()
+ unfilteredSubscriptions
+ .map { it.filterBasedOnProvisioning().filterBasedOnNtn() }
+ .distinctUntilChanged()
- private fun applyProvisioningFilter(subs: List<SubscriptionModel>): List<SubscriptionModel> =
+ private fun List<SubscriptionModel>.filterBasedOnProvisioning(): List<SubscriptionModel> =
if (!featureFlagsClassic.isEnabled(FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS)) {
- subs
+ this
} else {
- subs.filter { it.profileClass != PROFILE_CLASS_PROVISIONING }
+ this.filter { it.profileClass != PROFILE_CLASS_PROVISIONING }
}
/**
+ * Subscriptions that exclusively support non-terrestrial networks should **never** directly
+ * show any iconography in the status bar. These subscriptions only exist to provide a backing
+ * for the device-based satellite connections, and the iconography for those connections are
+ * already being handled in
+ * [com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository]. We
+ * need to filter out those subscriptions here so we guarantee the subscription never turns into
+ * an icon. See b/336881301.
+ */
+ private fun List<SubscriptionModel>.filterBasedOnNtn(): List<SubscriptionModel> {
+ return this.filter { !it.isExclusivelyNonTerrestrial }
+ }
+
+ /**
* Generally, SystemUI wants to show iconography for each subscription that is listed by
* [SubscriptionManager]. However, in the case of opportunistic subscriptions, we want to only
* show a single representation of the pair of subscriptions. The docs define opportunistic as:
@@ -204,12 +216,8 @@
subscriptionsBasedFilteredSubs,
mobileConnectionsRepo.activeMobileDataSubscriptionId,
connectivityRepository.vcnSubId,
- ) { unfilteredSubs, activeId, vcnSubId ->
- filterSubsBasedOnOpportunistic(
- unfilteredSubs,
- activeId,
- vcnSubId,
- )
+ ) { preFilteredSubs, activeId, vcnSubId ->
+ filterSubsBasedOnOpportunistic(preFilteredSubs, activeId, vcnSubId)
}
.distinctUntilChanged()
.logDiffsForTable(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
index 2670a95..fa8a7d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt
@@ -253,6 +253,7 @@
if (nextList.isEmpty()) {
log { "NO MORE TO SHOW" }
+ previousHunKey = ""
return
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
index 41974f4..8e4c155 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt
@@ -46,7 +46,8 @@
@RunWithLooper
class ActivityTransitionAnimatorTest : SysuiTestCase() {
private val transitionContainer = LinearLayout(mContext)
- private val testTransitionAnimator = fakeTransitionAnimator()
+ private val mainExecutor = context.mainExecutor
+ private val testTransitionAnimator = fakeTransitionAnimator(mainExecutor)
@Mock lateinit var callback: ActivityTransitionAnimator.Callback
@Mock lateinit var listener: ActivityTransitionAnimator.Listener
@Spy private val controller = TestTransitionAnimatorController(transitionContainer)
@@ -59,9 +60,10 @@
fun setup() {
activityTransitionAnimator =
ActivityTransitionAnimator(
+ mainExecutor,
testTransitionAnimator,
testTransitionAnimator,
- disableWmTimeout = true
+ disableWmTimeout = true,
)
activityTransitionAnimator.callback = callback
activityTransitionAnimator.addListener(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
index d84a578..e14762cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogTransitionAnimatorTest.kt
@@ -156,6 +156,7 @@
fun testActivityLaunchWhenLockedWithoutAlternateAuth() {
val dialogTransitionAnimator =
fakeDialogTransitionAnimator(
+ mainExecutor = mContext.mainExecutor,
isUnlocked = false,
isShowingAlternateAuthOnUnlock = false,
interactionJankMonitor = kosmos.interactionJankMonitor)
@@ -166,6 +167,7 @@
@Test
fun testActivityLaunchWhenLockedWithAlternateAuth() {
val dialogTransitionAnimator = fakeDialogTransitionAnimator(
+ mainExecutor = mContext.mainExecutor,
isUnlocked = false,
isShowingAlternateAuthOnUnlock = true,
interactionJankMonitor = kosmos.interactionJankMonitor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
index e64df90..259ece9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TransitionAnimatorTest.kt
@@ -25,6 +25,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
+import com.android.systemui.concurrency.fakeExecutor
+import com.android.systemui.kosmos.Kosmos
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -58,9 +60,11 @@
)
}
+ private val kosmos = Kosmos()
private val pathManager = GoldenPathManager(context, GOLDENS_PATH, pathConfig = PathConfig())
private val transitionAnimator =
TransitionAnimator(
+ kosmos.fakeExecutor,
ActivityTransitionAnimator.TIMINGS,
ActivityTransitionAnimator.INTERPOLATORS
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
index 5caa146..0d01472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/DefaultUdfpsTouchOverlayViewModelTest.kt
@@ -16,97 +16,95 @@
package com.android.systemui.biometrics.ui.viewmodel
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
-import com.android.systemui.SysUITestComponent
-import com.android.systemui.SysUITestModule
import com.android.systemui.SysuiTestCase
-import com.android.systemui.TestMocksModule
-import com.android.systemui.biometrics.domain.BiometricsDomainLayerModule
-import com.android.systemui.collectLastValue
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FakeFeatureFlagsClassicModule
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.Flags
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.flags.parameterizeSceneContainerFlag
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.StatusBarState
-import com.android.systemui.runCurrent
-import com.android.systemui.runTest
-import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.phone.SystemUIDialogManager
-import com.android.systemui.user.domain.UserDomainLayerModule
-import com.android.systemui.util.mockito.mock
+import com.android.systemui.statusbar.phone.systemUIDialogManager
+import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
-import dagger.BindsInstance
-import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
-@RunWith(JUnit4::class)
-class DefaultUdfpsTouchOverlayViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class DefaultUdfpsTouchOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
+
+ private val kosmos =
+ testKosmos().apply {
+ fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, true) }
+ }
+ private val testScope = kosmos.testScope
+
@Captor
private lateinit var sysuiDialogListenerCaptor: ArgumentCaptor<SystemUIDialogManager.Listener>
- private var systemUIDialogManager: SystemUIDialogManager = mock()
+ private var systemUIDialogManager = kosmos.systemUIDialogManager
+ private val keyguardRepository = kosmos.fakeKeyguardRepository
+
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+ private lateinit var underTest: DefaultUdfpsTouchOverlayViewModel
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return parameterizeSceneContainerFlag()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
+ underTest =
+ DefaultUdfpsTouchOverlayViewModel(
+ kosmos.shadeInteractor,
+ systemUIDialogManager,
+ )
}
- @SysUISingleton
- @Component(
- modules =
- [
- SysUITestModule::class,
- UserDomainLayerModule::class,
- BiometricsDomainLayerModule::class,
- ]
- )
- interface TestComponent : SysUITestComponent<DefaultUdfpsTouchOverlayViewModel> {
- val keyguardRepository: FakeKeyguardRepository
- val shadeRepository: FakeShadeRepository
- @Component.Factory
- interface Factory {
- fun create(
- @BindsInstance test: SysuiTestCase,
- featureFlags: FakeFeatureFlagsClassicModule,
- mocks: TestMocksModule,
- ): TestComponent
- }
- }
-
- private fun TestComponent.shadeExpanded(expanded: Boolean) {
+ private fun shadeExpanded(expanded: Boolean) {
if (expanded) {
- shadeRepository.setLegacyShadeExpansion(1f)
- shadeRepository.setLegacyShadeTracking(false)
- shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(true)
+ shadeTestUtil.setShadeExpansion(1f)
+ shadeTestUtil.setTracking(false)
+ shadeTestUtil.setLegacyExpandedOrAwaitingInputTransfer(true)
} else {
keyguardRepository.setStatusBarState(StatusBarState.SHADE)
- shadeRepository.setLegacyShadeExpansion(0f)
- shadeRepository.setLegacyShadeTracking(false)
- shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(false)
+ shadeTestUtil.setShadeExpansion(0f)
+ shadeTestUtil.setTracking(false)
+ shadeTestUtil.setLegacyExpandedOrAwaitingInputTransfer(false)
}
}
- private val testComponent: TestComponent =
- DaggerDefaultUdfpsTouchOverlayViewModelTest_TestComponent.factory()
- .create(
- test = this,
- featureFlags =
- FakeFeatureFlagsClassicModule { set(Flags.FULL_SCREEN_USER_SWITCHER, true) },
- mocks = TestMocksModule(systemUIDialogManager = systemUIDialogManager),
- )
-
@Test
+ @BrokenWithSceneContainer(339465026)
fun shadeNotExpanded_noDialogShowing_shouldHandleTouchesTrue() =
- testComponent.runTest {
+ testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
runCurrent()
@@ -120,7 +118,7 @@
@Test
fun shadeNotExpanded_dialogShowing_shouldHandleTouchesFalse() =
- testComponent.runTest {
+ testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
runCurrent()
@@ -134,7 +132,7 @@
@Test
fun shadeExpanded_noDialogShowing_shouldHandleTouchesFalse() =
- testComponent.runTest {
+ testScope.runTest {
val shouldHandleTouches by collectLastValue(underTest.shouldHandleTouches)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
index 6a0462b..c39c3fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/deviceentry/domain/ui/viewmodel/UdfpsAccessibilityOverlayViewModelTest.kt
@@ -16,7 +16,7 @@
package com.android.systemui.deviceentry.domain.ui.viewmodel
-import androidx.test.ext.junit.runners.AndroidJUnit4
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository
@@ -24,7 +24,9 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.data.ui.viewmodel.deviceEntryUdfpsAccessibilityOverlayViewModel
+import com.android.systemui.deviceentry.ui.viewmodel.DeviceEntryUdfpsAccessibilityOverlayViewModel
import com.android.systemui.flags.Flags.FULL_SCREEN_USER_SWITCHER
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.deviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.fakeBiometricSettingsRepository
@@ -34,19 +36,22 @@
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.ui.viewmodel.fakeDeviceEntryIconViewModelTransition
import com.android.systemui.kosmos.testScope
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlin.test.Test
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.runner.RunWith
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(AndroidJUnit4::class)
-class UdfpsAccessibilityOverlayViewModelTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class UdfpsAccessibilityOverlayViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(FULL_SCREEN_USER_SWITCHER, false) }
@@ -59,8 +64,27 @@
private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
private val deviceEntryFingerprintAuthRepository = kosmos.deviceEntryFingerprintAuthRepository
private val deviceEntryRepository = kosmos.fakeDeviceEntryRepository
- private val shadeRepository = kosmos.fakeShadeRepository
- private val underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
+
+ private lateinit var underTest: DeviceEntryUdfpsAccessibilityOverlayViewModel
+
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags)
+ }
+
+ @Before
+ fun setup() {
+ underTest = kosmos.deviceEntryUdfpsAccessibilityOverlayViewModel
+ }
@Test
fun visible() =
@@ -142,7 +166,7 @@
)
// Shade not expanded
- shadeRepository.qsExpansion.value = 0f
- shadeRepository.lockscreenShadeExpansion.value = 0f
+ shadeTestUtil.setQsExpansion(0f)
+ shadeTestUtil.setLockscreenShadeExpansion(0f)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index 1dc58d1..687e91a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
+import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.keyguard.KeyguardSecurityModel
@@ -29,7 +30,9 @@
import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dock.fakeDockManager
+import com.android.systemui.flags.BrokenWithSceneContainer
import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.andSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeCommandQueue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -46,7 +49,8 @@
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
import com.android.systemui.power.domain.interactor.powerInteractor
-import com.android.systemui.shade.data.repository.fakeShadeRepository
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.commandQueue
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
@@ -61,13 +65,14 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.junit.runners.JUnit4
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.reset
import org.mockito.Mockito.spy
import org.mockito.MockitoAnnotations
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4
+import platform.test.runner.parameterized.Parameters
/**
* Class for testing user journeys through the interactors. They will all be activated during setup,
@@ -75,8 +80,8 @@
*/
@ExperimentalCoroutinesApi
@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardTransitionScenariosTest : SysuiTestCase() {
+@RunWith(ParameterizedAndroidJunit4::class)
+class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository())
@@ -87,7 +92,7 @@
private val keyguardRepository = kosmos.fakeKeyguardRepository
private val bouncerRepository = kosmos.fakeKeyguardBouncerRepository
private var commandQueue = kosmos.fakeCommandQueue
- private val shadeRepository = kosmos.fakeShadeRepository
+ private val shadeTestUtil by lazy { kosmos.shadeTestUtil }
private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
private lateinit var featureFlags: FakeFeatureFlags
@@ -112,6 +117,18 @@
private val communalInteractor = kosmos.communalInteractor
private val dockManager = kosmos.fakeDockManager
+ companion object {
+ @JvmStatic
+ @Parameters(name = "{0}")
+ fun getParams(): List<FlagsParameterization> {
+ return FlagsParameterization.allCombinationsOf().andSceneContainer()
+ }
+ }
+
+ init {
+ mSetFlagsRule.setFlagsParameterization(flags!!)
+ }
+
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -119,9 +136,11 @@
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
- mSetFlagsRule.disableFlags(
- Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
- )
+ if (!SceneContainerFlag.isEnabled) {
+ mSetFlagsRule.disableFlags(
+ Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR,
+ )
+ }
featureFlags = FakeFeatureFlags()
fromLockscreenTransitionInteractor.start()
@@ -210,6 +229,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun lockscreenToDreaming() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
@@ -238,6 +258,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun lockscreenToDreamingLockscreenHosted() =
testScope.runTest {
// GIVEN a device that is not dreaming or dozing
@@ -527,6 +548,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun dozingToGoneWithUnlock() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
@@ -706,6 +728,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun goneToLockscreen() =
testScope.runTest {
// GIVEN a prior transition has run to GONE
@@ -755,6 +778,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun goneToGlanceableHub() =
testScope.runTest {
// GIVEN a prior transition has run to GONE
@@ -897,6 +921,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun alternateBouncerToGone() =
testScope.runTest {
// GIVEN a prior transition has run to ALTERNATE_BOUNCER
@@ -1135,6 +1160,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun occludedToGone() =
testScope.runTest {
// GIVEN a device on lockscreen
@@ -1165,6 +1191,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun occludedToLockscreen() =
testScope.runTest {
// GIVEN a device on lockscreen
@@ -1193,6 +1220,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun occludedToGlanceableHub() =
testScope.runTest {
// GIVEN a device on lockscreen
@@ -1229,6 +1257,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun occludedToGlanceableHubWhenInitiallyOnHub() =
testScope.runTest {
// GIVEN a device on lockscreen and communal is available
@@ -1314,6 +1343,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun primaryBouncerToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to PRIMARY_BOUNCER
@@ -1339,6 +1369,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun dozingToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to DOZING
@@ -1364,6 +1395,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun dreamingToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to DREAMING
@@ -1484,6 +1516,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun lockscreenToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to LOCKSCREEN
@@ -1507,6 +1540,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun aodToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to AOD
@@ -1553,6 +1587,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun lockscreenToOccluded_fromCameraGesture() =
testScope.runTest {
// GIVEN a prior transition has run to LOCKSCREEN
@@ -1586,6 +1621,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun lockscreenToPrimaryBouncerDragging() =
testScope.runTest {
// GIVEN a prior transition has run to LOCKSCREEN
@@ -1595,8 +1631,8 @@
// GIVEN the keyguard is showing locked
keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
runCurrent()
- shadeRepository.setLegacyShadeTracking(true)
- shadeRepository.setLegacyShadeExpansion(.9f)
+ shadeTestUtil.setTracking(true)
+ shadeTestUtil.setShadeExpansion(.9f)
runCurrent()
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
@@ -1613,8 +1649,8 @@
// WHEN the user stops dragging and shade is back to expanded
clearInvocations(transitionRepository)
runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.PRIMARY_BOUNCER)
- shadeRepository.setLegacyShadeTracking(false)
- shadeRepository.setLegacyShadeExpansion(1f)
+ shadeTestUtil.setTracking(false)
+ shadeTestUtil.setShadeExpansion(1f)
runCurrent()
// THEN a transition from LOCKSCREEN => PRIMARY_BOUNCER should occur
@@ -1803,6 +1839,7 @@
}
@Test
+ @BrokenWithSceneContainer(339465026)
fun glanceableHubToOccluded() =
testScope.runTest {
// GIVEN a prior transition has run to GLANCEABLE_HUB
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
index b1a8dd1..a77169e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt
@@ -18,20 +18,29 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.DisableSceneContainer
+import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.data.repository.sceneContainerRepository
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -57,14 +66,22 @@
.thenReturn(surfaceBehindIsAnimatingFlow)
}
- private val underTest = kosmos.windowManagerLockscreenVisibilityInteractor
+ private val underTest = lazy { kosmos.windowManagerLockscreenVisibilityInteractor }
private val testScope = kosmos.testScope
private val transitionRepository = kosmos.fakeKeyguardTransitionRepository
+ @Before
+ fun setUp() {
+ // lazy value needs to be called here otherwise flow collection misbehaves
+ underTest.value
+ kosmos.sceneContainerRepository.setTransitionState(sceneTransitions)
+ }
+
@Test
+ @DisableSceneContainer
fun surfaceBehindVisibility_switchesToCorrectFlow() =
testScope.runTest {
- val values by collectValues(underTest.surfaceBehindVisibility)
+ val values by collectValues(underTest.value.surfaceBehindVisibility)
// Start on LOCKSCREEN.
transitionRepository.sendTransitionStep(
@@ -170,9 +187,10 @@
}
@Test
+ @DisableSceneContainer
fun testUsingGoingAwayAnimation_duringTransitionToGone() =
testScope.runTest {
- val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
+ val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation)
// Start on LOCKSCREEN.
transitionRepository.sendTransitionStep(
@@ -230,9 +248,10 @@
}
@Test
+ @DisableSceneContainer
fun testNotUsingGoingAwayAnimation_evenWhenAnimating_ifStateIsNotGone() =
testScope.runTest {
- val values by collectValues(underTest.usingKeyguardGoingAwayAnimation)
+ val values by collectValues(underTest.value.usingKeyguardGoingAwayAnimation)
// Start on LOCKSCREEN.
transitionRepository.sendTransitionStep(
@@ -319,9 +338,10 @@
}
@Test
+ @DisableSceneContainer
fun lockscreenVisibility_visibleWhenGone() =
testScope.runTest {
- val values by collectValues(underTest.lockscreenVisibility)
+ val values by collectValues(underTest.value.lockscreenVisibility)
// Start on LOCKSCREEN.
transitionRepository.sendTransitionStep(
@@ -385,9 +405,10 @@
}
@Test
+ @DisableSceneContainer
fun testLockscreenVisibility_usesFromState_ifCanceled() =
testScope.runTest {
- val values by collectValues(underTest.lockscreenVisibility)
+ val values by collectValues(underTest.value.lockscreenVisibility)
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
@@ -486,9 +507,10 @@
* state during the AOD/isAsleep -> GONE transition is AOD (where lockscreen visibility = true).
*/
@Test
+ @DisableSceneContainer
fun testLockscreenVisibility_falseDuringTransitionToGone_fromCanceledGone() =
testScope.runTest {
- val values by collectValues(underTest.lockscreenVisibility)
+ val values by collectValues(underTest.value.lockscreenVisibility)
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
@@ -587,11 +609,11 @@
)
}
- /** */
@Test
+ @DisableSceneContainer
fun testLockscreenVisibility_trueDuringTransitionToGone_fromNotCanceledGone() =
testScope.runTest {
- val values by collectValues(underTest.lockscreenVisibility)
+ val values by collectValues(underTest.value.lockscreenVisibility)
transitionRepository.sendTransitionSteps(
from = KeyguardState.LOCKSCREEN,
@@ -702,4 +724,109 @@
values
)
}
+
+ @Test
+ @EnableSceneContainer
+ fun sceneContainer_lockscreenVisibility_visibleWhenNotGone() =
+ testScope.runTest {
+ val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+
+ sceneTransitions.value = lsToGone
+ assertThat(lockscreenVisibility).isTrue()
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ assertThat(lockscreenVisibility).isFalse()
+
+ sceneTransitions.value = goneToLs
+ assertThat(lockscreenVisibility).isFalse()
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+ assertThat(lockscreenVisibility).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun sceneContainer_lockscreenVisibility_notVisibleWhenReturningToGone() =
+ testScope.runTest {
+ val lockscreenVisibility by collectLastValue(underTest.value.lockscreenVisibility)
+
+ sceneTransitions.value = goneToLs
+ assertThat(lockscreenVisibility).isFalse()
+
+ sceneTransitions.value = lsToGone
+ assertThat(lockscreenVisibility).isFalse()
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ assertThat(lockscreenVisibility).isFalse()
+
+ sceneTransitions.value = goneToLs
+ assertThat(lockscreenVisibility).isFalse()
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Lockscreen)
+ assertThat(lockscreenVisibility).isTrue()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun sceneContainer_usingGoingAwayAnimation_duringTransitionToGone() =
+ testScope.runTest {
+ val usingKeyguardGoingAwayAnimation by
+ collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation)
+
+ sceneTransitions.value = lsToGone
+ assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ assertThat(usingKeyguardGoingAwayAnimation).isFalse()
+ }
+
+ @Test
+ @EnableSceneContainer
+ fun sceneContainer_usingGoingAwayAnimation_surfaceBehindIsAnimating() =
+ testScope.runTest {
+ val usingKeyguardGoingAwayAnimation by
+ collectLastValue(underTest.value.usingKeyguardGoingAwayAnimation)
+
+ sceneTransitions.value = lsToGone
+ surfaceBehindIsAnimatingFlow.emit(true)
+ assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+ sceneTransitions.value = ObservableTransitionState.Idle(Scenes.Gone)
+ assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+ sceneTransitions.value = goneToLs
+ assertThat(usingKeyguardGoingAwayAnimation).isTrue()
+
+ surfaceBehindIsAnimatingFlow.emit(false)
+ assertThat(usingKeyguardGoingAwayAnimation).isFalse()
+ }
+
+ companion object {
+ private val progress = MutableStateFlow(0f)
+
+ private val sceneTransitions =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(Scenes.Lockscreen)
+ )
+
+ private val lsToGone =
+ ObservableTransitionState.Transition(
+ Scenes.Lockscreen,
+ Scenes.Gone,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+
+ private val goneToLs =
+ ObservableTransitionState.Transition(
+ Scenes.Gone,
+ Scenes.Lockscreen,
+ flowOf(Scenes.Lockscreen),
+ progress,
+ false,
+ flowOf(false)
+ )
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
index 0bca367..f61ddeb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelTest.kt
@@ -19,6 +19,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.accessibility.data.repository.fakeAccessibilityRepository
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.coroutines.collectLastValue
@@ -150,6 +151,51 @@
assertThat(iconType).isEqualTo(DeviceEntryIconView.IconType.NONE)
}
+ fun accessibilityDelegateHint_accessibilityNotEnabled() =
+ testScope.runTest {
+ val accessibilityDelegateHint by collectLastValue(underTest.accessibilityDelegateHint)
+ kosmos.fakeAccessibilityRepository.isEnabled.value = false
+ assertThat(accessibilityDelegateHint)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+ }
+
+ @Test
+ fun accessibilityDelegateHint_accessibilityEnabled_locked() =
+ testScope.runTest {
+ val accessibilityDelegateHint by collectLastValue(underTest.accessibilityDelegateHint)
+ kosmos.fakeAccessibilityRepository.isEnabled.value = true
+
+ // interactive lock icon
+ keyguardRepository.setKeyguardDismissible(false)
+ fingerprintPropertyRepository.supportsUdfps()
+
+ assertThat(accessibilityDelegateHint)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.AUTHENTICATE)
+
+ // non-interactive lock icon
+ keyguardRepository.setKeyguardDismissible(false)
+ fingerprintPropertyRepository.supportsRearFps()
+
+ assertThat(accessibilityDelegateHint)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.NONE)
+ }
+
+ @Test
+ fun accessibilityDelegateHint_accessibilityEnabled_unlocked() =
+ testScope.runTest {
+ val accessibilityDelegateHint by collectLastValue(underTest.accessibilityDelegateHint)
+ kosmos.fakeAccessibilityRepository.isEnabled.value = true
+
+ // interactive unlock icon
+ keyguardRepository.setKeyguardDismissible(true)
+ fingerprintPropertyRepository.supportsUdfps()
+ advanceTimeBy(UNLOCKED_DELAY_MS * 2) // wait for unlocked delay
+ runCurrent()
+
+ assertThat(accessibilityDelegateHint)
+ .isEqualTo(DeviceEntryIconView.AccessibilityHintType.ENTER)
+ }
+
private fun deviceEntryIconTransitionAlpha(alpha: Float) {
deviceEntryIconTransition.setDeviceEntryParentViewAlpha(alpha)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 1881a9e..16421a0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -88,7 +88,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardBottomAreaViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var expandable: Expandable
@Mock private lateinit var burnInHelperWrapper: BurnInHelperWrapper
@@ -115,7 +115,7 @@
private val kosmos = testKosmos()
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 0c98cff..768d446 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -53,7 +53,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardClockViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardClockViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
val kosmos = testKosmos()
val testScope = kosmos.testScope
val underTest = kosmos.keyguardClockViewModel
@@ -67,7 +67,7 @@
var faceConfig = ClockFaceConfig()
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index a867b0f..45d0102 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -102,7 +102,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWithLooper(setAsMainLooper = true)
-class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
@Mock private lateinit var view: NotificationShadeWindowView
@Mock private lateinit var sysuiStatusBarStateController: SysuiStatusBarStateController
@@ -160,7 +160,7 @@
private lateinit var featureFlagsClassic: FakeFeatureFlagsClassic
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 347620a..83ad18b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -55,7 +55,7 @@
@RunWith(ParameterizedAndroidJunit4::class)
@SmallTest
@EnableFlags(FooterViewRefactor.FLAG_NAME)
-class FooterViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class FooterViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos =
testKosmos().apply {
fakeFeatureFlagsClassic.apply { set(Flags.FULL_SCREEN_USER_SWITCHER, false) }
@@ -79,7 +79,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
index 9b4f931..cb40f72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBypassControllerTest.kt
@@ -66,7 +66,7 @@
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@TestableLooper.RunWithLooper
-class KeyguardBypassControllerTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardBypassControllerTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val featureFlags = FakeFeatureFlags()
@@ -92,7 +92,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Captor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index b5525b1..36df61d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -295,6 +295,50 @@
}
@Test
+ fun subscriptions_subIsOnlyNtn_modelHasExclusivelyNtnTrue() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.subscriptions)
+
+ val onlyNtnSub =
+ mock<SubscriptionInfo>().also {
+ whenever(it.isOnlyNonTerrestrialNetwork).thenReturn(true)
+ whenever(it.subscriptionId).thenReturn(45)
+ whenever(it.groupUuid).thenReturn(GROUP_1)
+ whenever(it.carrierName).thenReturn("NTN only")
+ whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+ }
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(onlyNtnSub))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isExclusivelyNonTerrestrial).isTrue()
+ }
+
+ @Test
+ fun subscriptions_subIsNotOnlyNtn_modelHasExclusivelyNtnFalse() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.subscriptions)
+
+ val notOnlyNtnSub =
+ mock<SubscriptionInfo>().also {
+ whenever(it.isOnlyNonTerrestrialNetwork).thenReturn(false)
+ whenever(it.subscriptionId).thenReturn(45)
+ whenever(it.groupUuid).thenReturn(GROUP_1)
+ whenever(it.carrierName).thenReturn("NTN only")
+ whenever(it.profileClass).thenReturn(PROFILE_CLASS_UNSET)
+ }
+
+ whenever(subscriptionManager.completeActiveSubscriptionInfoList)
+ .thenReturn(listOf(notOnlyNtnSub))
+ getSubscriptionCallback().onSubscriptionsChanged()
+
+ assertThat(latest).hasSize(1)
+ assertThat(latest!![0].isExclusivelyNonTerrestrial).isFalse()
+ }
+
+ @Test
fun testSubscriptions_carrierMergedOnly_listHasCarrierMerged() =
testScope.runTest {
val latest by collectLastValue(underTest.subscriptions)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
index 0b14be1..0f9cbfa 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt
@@ -42,14 +42,11 @@
import com.google.common.truth.Truth.assertThat
import java.util.UUID
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.launchIn
-import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.advanceTimeBy
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import kotlinx.coroutines.yield
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mockito.Mock
@@ -68,7 +65,7 @@
set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true)
}
- private val testDispatcher = UnconfinedTestDispatcher()
+ private val testDispatcher = StandardTestDispatcher()
private val testScope = TestScope(testDispatcher)
private val tableLogBuffer =
@@ -113,17 +110,12 @@
)
}
- @After fun tearDown() {}
-
@Test
fun filteredSubscriptions_default() =
testScope.runTest {
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf<SubscriptionModel>())
-
- job.cancel()
}
// Based on the logic from the old pipeline, we'll never filter subs when there are more than 2
@@ -133,12 +125,9 @@
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf(SUB_1, SUB_3_OPP, SUB_4_OPP))
-
- job.cancel()
}
@Test
@@ -146,12 +135,9 @@
testScope.runTest {
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf(SUB_1, SUB_2))
-
- job.cancel()
}
@Test
@@ -160,12 +146,9 @@
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf(SUB_3_OPP, SUB_4_OPP))
-
- job.cancel()
}
@Test
@@ -180,12 +163,9 @@
connectionsRepository.setSubscriptions(listOf(sub1, sub2))
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf(sub1, sub2))
-
- job.cancel()
}
@Test
@@ -202,13 +182,10 @@
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
// Filtered subscriptions should show the active one when the config is false
assertThat(latest).isEqualTo(listOf(sub3))
-
- job.cancel()
}
@Test
@@ -225,13 +202,10 @@
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
// Filtered subscriptions should show the active one when the config is false
assertThat(latest).isEqualTo(listOf(sub4))
-
- job.cancel()
}
@Test
@@ -248,14 +222,11 @@
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
// true
assertThat(latest).isEqualTo(listOf(sub1))
-
- job.cancel()
}
@Test
@@ -272,14 +243,11 @@
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(true)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
// Filtered subscriptions should show the primary (non-opportunistic) if the config is
// true
assertThat(latest).isEqualTo(listOf(sub1))
-
- job.cancel()
}
@Test
@@ -297,12 +265,9 @@
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf(sub3))
-
- job.cancel()
}
@Test
@@ -320,12 +285,9 @@
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
.thenReturn(false)
- var latest: List<SubscriptionModel>? = null
- val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.filteredSubscriptions)
assertThat(latest).isEqualTo(listOf(sub1))
-
- job.cancel()
}
@Test
@@ -446,313 +408,345 @@
}
@Test
+ fun filteredSubscriptions_subNotExclusivelyNonTerrestrial_hasSub() =
+ testScope.runTest {
+ val notExclusivelyNonTerrestrialSub =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = false,
+ subscriptionId = 5,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+
+ connectionsRepository.setSubscriptions(listOf(notExclusivelyNonTerrestrialSub))
+
+ val latest by collectLastValue(underTest.filteredSubscriptions)
+
+ assertThat(latest).isEqualTo(listOf(notExclusivelyNonTerrestrialSub))
+ }
+
+ @Test
+ fun filteredSubscriptions_subExclusivelyNonTerrestrial_doesNotHaveSub() =
+ testScope.runTest {
+ val exclusivelyNonTerrestrialSub =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = true,
+ subscriptionId = 5,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+
+ connectionsRepository.setSubscriptions(listOf(exclusivelyNonTerrestrialSub))
+
+ val latest by collectLastValue(underTest.filteredSubscriptions)
+
+ assertThat(latest).isEmpty()
+ }
+
+ @Test
+ fun filteredSubscription_mixOfExclusivelyNonTerrestrialAndOther_hasOtherSubsOnly() =
+ testScope.runTest {
+ val exclusivelyNonTerrestrialSub =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = true,
+ subscriptionId = 5,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ val otherSub1 =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = false,
+ subscriptionId = 1,
+ carrierName = "Carrier 1",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+ val otherSub2 =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = false,
+ subscriptionId = 2,
+ carrierName = "Carrier 2",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+
+ connectionsRepository.setSubscriptions(
+ listOf(otherSub1, exclusivelyNonTerrestrialSub, otherSub2)
+ )
+
+ val latest by collectLastValue(underTest.filteredSubscriptions)
+
+ assertThat(latest).isEqualTo(listOf(otherSub1, otherSub2))
+ }
+
+ @Test
+ fun filteredSubscriptions_exclusivelyNonTerrestrialSub_andOpportunistic_bothFiltersHappen() =
+ testScope.runTest {
+ // Exclusively non-terrestrial sub
+ val exclusivelyNonTerrestrialSub =
+ SubscriptionModel(
+ isExclusivelyNonTerrestrial = true,
+ subscriptionId = 5,
+ carrierName = "Carrier 5",
+ profileClass = PROFILE_CLASS_UNSET,
+ )
+
+ // Opportunistic subs
+ val (sub3, sub4) =
+ createSubscriptionPair(
+ subscriptionIds = Pair(SUB_3_ID, SUB_4_ID),
+ opportunistic = Pair(true, true),
+ grouped = true,
+ )
+
+ // WHEN both an exclusively non-terrestrial sub and opportunistic sub pair is included
+ connectionsRepository.setSubscriptions(listOf(sub3, sub4, exclusivelyNonTerrestrialSub))
+ connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
+
+ val latest by collectLastValue(underTest.filteredSubscriptions)
+
+ // THEN both the only-non-terrestrial sub and the non-active sub are filtered out,
+ // leaving only sub3.
+ assertThat(latest).isEqualTo(listOf(sub3))
+ }
+
+ @Test
fun activeDataConnection_turnedOn() =
testScope.runTest {
CONNECTION_1.setDataEnabled(true)
- var latest: Boolean? = null
- val job =
- underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
+
+ val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
assertThat(latest).isTrue()
-
- job.cancel()
}
@Test
fun activeDataConnection_turnedOff() =
testScope.runTest {
CONNECTION_1.setDataEnabled(true)
- var latest: Boolean? = null
- val job =
- underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
CONNECTION_1.setDataEnabled(false)
- yield()
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun activeDataConnection_invalidSubId() =
testScope.runTest {
- var latest: Boolean? = null
- val job =
- underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.activeDataConnectionHasDataEnabled)
connectionsRepository.setActiveMobileDataSubscriptionId(INVALID_SUBSCRIPTION_ID)
- yield()
// An invalid active subId should tell us that data is off
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun failedConnection_default_validated_notFailed() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
- yield()
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun failedConnection_notDefault_notValidated_notFailed() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = false
connectionsRepository.defaultConnectionIsValidated.value = false
- yield()
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun failedConnection_default_notValidated_failed() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = false
- yield()
assertThat(latest).isTrue()
-
- job.cancel()
}
@Test
fun failedConnection_carrierMergedDefault_notValidated_failed() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.hasCarrierMergedConnection.value = true
connectionsRepository.defaultConnectionIsValidated.value = false
- yield()
assertThat(latest).isTrue()
-
- job.cancel()
}
/** Regression test for b/275076959. */
@Test
fun failedConnection_dataSwitchInSameGroup_notFailed() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
+ runCurrent()
// WHEN there's a data change in the same subscription group
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
connectionsRepository.defaultConnectionIsValidated.value = false
+ runCurrent()
// THEN the default connection is *not* marked as failed because of forced validation
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun failedConnection_dataSwitchNotInSameGroup_isFailed() =
testScope.runTest {
- var latestConnectionFailed: Boolean? = null
- val job =
- underTest.isDefaultConnectionFailed
- .onEach { latestConnectionFailed = it }
- .launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
+
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
+ runCurrent()
// WHEN the connection is invalidated without a activeSubChangedInGroupEvent
connectionsRepository.defaultConnectionIsValidated.value = false
// THEN the connection is immediately marked as failed
- assertThat(latestConnectionFailed).isTrue()
-
- job.cancel()
+ assertThat(latest).isTrue()
}
@Test
fun alwaysShowDataRatIcon_configHasTrue() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
val config = MobileMappings.Config()
config.alwaysShowDataRatIcon = true
connectionsRepository.defaultDataSubRatConfig.value = config
- yield()
assertThat(latest).isTrue()
-
- job.cancel()
}
@Test
fun alwaysShowDataRatIcon_configHasFalse() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.alwaysShowDataRatIcon)
val config = MobileMappings.Config()
config.alwaysShowDataRatIcon = false
connectionsRepository.defaultDataSubRatConfig.value = config
- yield()
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun alwaysUseCdmaLevel_configHasTrue() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
val config = MobileMappings.Config()
config.alwaysShowCdmaRssi = true
connectionsRepository.defaultDataSubRatConfig.value = config
- yield()
assertThat(latest).isTrue()
-
- job.cancel()
}
@Test
fun alwaysUseCdmaLevel_configHasFalse() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.alwaysUseCdmaLevel)
val config = MobileMappings.Config()
config.alwaysShowCdmaRssi = false
connectionsRepository.defaultDataSubRatConfig.value = config
- yield()
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun isSingleCarrier_zeroSubscriptions_false() =
testScope.runTest {
- var latest: Boolean? = true
- val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(emptyList())
- assertThat(latest).isFalse()
- job.cancel()
+ assertThat(latest).isFalse()
}
@Test
fun isSingleCarrier_oneSubscription_true() =
testScope.runTest {
- var latest: Boolean? = false
- val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1))
- assertThat(latest).isTrue()
- job.cancel()
+ assertThat(latest).isTrue()
}
@Test
fun isSingleCarrier_twoSubscriptions_false() =
testScope.runTest {
- var latest: Boolean? = true
- val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
- assertThat(latest).isFalse()
- job.cancel()
+ assertThat(latest).isFalse()
}
@Test
fun isSingleCarrier_updates() =
testScope.runTest {
- var latest: Boolean? = false
- val job = underTest.isSingleCarrier.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isSingleCarrier)
connectionsRepository.setSubscriptions(listOf(SUB_1))
assertThat(latest).isTrue()
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedFalse_false() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = false
connectionsRepository.hasCarrierMergedConnection.value = false
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
fun mobileIsDefault_mobileTrueAndCarrierMergedFalse_true() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.hasCarrierMergedConnection.value = false
assertThat(latest).isTrue()
-
- job.cancel()
}
/** Regression test for b/272586234. */
@Test
fun mobileIsDefault_mobileFalseAndCarrierMergedTrue_true() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = false
connectionsRepository.hasCarrierMergedConnection.value = true
assertThat(latest).isTrue()
-
- job.cancel()
}
@Test
fun mobileIsDefault_updatesWhenRepoUpdates() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.mobileIsDefault.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.mobileIsDefault)
connectionsRepository.mobileIsDefault.value = true
assertThat(latest).isTrue()
@@ -762,8 +756,6 @@
connectionsRepository.hasCarrierMergedConnection.value = true
assertThat(latest).isTrue()
-
- job.cancel()
}
// The data switch tests are mostly testing the [forcingCellularValidation] flow, but that flow
@@ -772,95 +764,79 @@
@Test
fun dataSwitch_inSameGroup_validatedMatchesPreviousValue_expiresAfter2s() =
testScope.runTest {
- var latestConnectionFailed: Boolean? = null
- val job =
- underTest.isDefaultConnectionFailed
- .onEach { latestConnectionFailed = it }
- .launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
+ runCurrent()
// Trigger a data change in the same subscription group that's not yet validated
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
connectionsRepository.defaultConnectionIsValidated.value = false
+ runCurrent()
// After 1s, the force validation bit is still present, so the connection is not marked
// as failed
advanceTimeBy(1000)
- assertThat(latestConnectionFailed).isFalse()
+ assertThat(latest).isFalse()
// After 2s, the force validation expires so the connection updates to failed
advanceTimeBy(1001)
- assertThat(latestConnectionFailed).isTrue()
-
- job.cancel()
+ assertThat(latest).isTrue()
}
@Test
fun dataSwitch_inSameGroup_notValidated_immediatelyMarkedAsFailed() =
testScope.runTest {
- var latestConnectionFailed: Boolean? = null
- val job =
- underTest.isDefaultConnectionFailed
- .onEach { latestConnectionFailed = it }
- .launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = false
+ runCurrent()
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
- assertThat(latestConnectionFailed).isTrue()
-
- job.cancel()
+ assertThat(latest).isTrue()
}
@Test
fun dataSwitch_loseValidation_thenSwitchHappens_clearsForcedBit() =
testScope.runTest {
- var latestConnectionFailed: Boolean? = null
- val job =
- underTest.isDefaultConnectionFailed
- .onEach { latestConnectionFailed = it }
- .launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
// GIVEN the network starts validated
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
+ runCurrent()
// WHEN a data change happens in the same group
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
// WHEN the validation bit is lost
connectionsRepository.defaultConnectionIsValidated.value = false
+ runCurrent()
// WHEN another data change happens in the same group
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
// THEN the forced validation bit is still used...
- assertThat(latestConnectionFailed).isFalse()
+ assertThat(latest).isFalse()
advanceTimeBy(1000)
- assertThat(latestConnectionFailed).isFalse()
+ assertThat(latest).isFalse()
// ... but expires after 2s
advanceTimeBy(1001)
- assertThat(latestConnectionFailed).isTrue()
-
- job.cancel()
+ assertThat(latest).isTrue()
}
@Test
fun dataSwitch_whileAlreadyForcingValidation_resetsClock() =
testScope.runTest {
- var latestConnectionFailed: Boolean? = null
- val job =
- underTest.isDefaultConnectionFailed
- .onEach { latestConnectionFailed = it }
- .launchIn(this)
+ val latest by collectLastValue(underTest.isDefaultConnectionFailed)
connectionsRepository.mobileIsDefault.value = true
connectionsRepository.defaultConnectionIsValidated.value = true
+ runCurrent()
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
@@ -869,44 +845,37 @@
// WHEN another change in same group event happens
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
connectionsRepository.defaultConnectionIsValidated.value = false
+ runCurrent()
// THEN the forced validation remains for exactly 2 more seconds from now
// 1.500s from second event
advanceTimeBy(1500)
- assertThat(latestConnectionFailed).isFalse()
+ assertThat(latest).isFalse()
// 2.001s from the second event
advanceTimeBy(501)
- assertThat(latestConnectionFailed).isTrue()
-
- job.cancel()
+ assertThat(latest).isTrue()
}
@Test
fun isForceHidden_repoHasMobileHidden_true() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isForceHidden)
connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE))
assertThat(latest).isTrue()
-
- job.cancel()
}
@Test
fun isForceHidden_repoDoesNotHaveMobileHidden_false() =
testScope.runTest {
- var latest: Boolean? = null
- val job = underTest.isForceHidden.onEach { latest = it }.launchIn(this)
+ val latest by collectLastValue(underTest.isForceHidden)
connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
assertThat(latest).isFalse()
-
- job.cancel()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
index cfa734a1..ab10bc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ui/viewmodel/KeyguardStatusBarViewModelTest.kt
@@ -47,7 +47,7 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(ParameterizedAndroidJunit4::class)
-class KeyguardStatusBarViewModelTest(flags: FlagsParameterization?) : SysuiTestCase() {
+class KeyguardStatusBarViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
private val keyguardRepository by lazy { kosmos.fakeKeyguardRepository }
@@ -66,7 +66,7 @@
}
init {
- mSetFlagsRule.setFlagsParameterization(flags!!)
+ mSetFlagsRule.setFlagsParameterization(flags)
}
@Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 56e5e29..aac3640 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -138,7 +138,6 @@
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
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.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
index 4085b1b..923b636 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/accessibility/data/repository/FakeAccessibilityRepository.kt
@@ -25,8 +25,9 @@
@SysUISingleton
class FakeAccessibilityRepository(
override val isTouchExplorationEnabled: MutableStateFlow<Boolean>,
+ override val isEnabled: MutableStateFlow<Boolean>,
) : AccessibilityRepository {
- @Inject constructor() : this(MutableStateFlow(false))
+ @Inject constructor() : this(MutableStateFlow(false), MutableStateFlow(false))
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
index 66c9afb..b23767e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/ActivityTransitionAnimatorKosmos.kt
@@ -17,5 +17,13 @@
package com.android.systemui.animation
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testCase
-val Kosmos.activityTransitionAnimator by Kosmos.Fixture { ActivityTransitionAnimator() }
+val Kosmos.activityTransitionAnimator by
+ Kosmos.Fixture {
+ ActivityTransitionAnimator(
+ // The main thread is checked in a bunch of places inside the different transitions
+ // animators, so we have to pass the real main executor here.
+ mainExecutor = testCase.context.mainExecutor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
index 77cb167..5a092f3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt
@@ -19,7 +19,13 @@
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testCase
val Kosmos.dialogTransitionAnimator by Fixture {
- fakeDialogTransitionAnimator(interactionJankMonitor = interactionJankMonitor)
+ fakeDialogTransitionAnimator(
+ // The main thread is checked in a bunch of places inside the different transitions
+ // animators, so we have to pass the real main executor here.
+ mainExecutor = testCase.context.mainExecutor,
+ interactionJankMonitor = interactionJankMonitor,
+ )
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
index 48b72d0..1709329 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
@@ -15,17 +15,20 @@
package com.android.systemui.animation
import com.android.internal.jank.InteractionJankMonitor
-import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
/** A [DialogTransitionAnimator] to be used in tests. */
@JvmOverloads
fun fakeDialogTransitionAnimator(
+ @Main mainExecutor: Executor,
isUnlocked: Boolean = true,
isShowingAlternateAuthOnUnlock: Boolean = false,
isPredictiveBackQsDialogAnim: Boolean = false,
interactionJankMonitor: InteractionJankMonitor,
): DialogTransitionAnimator {
return DialogTransitionAnimator(
+ mainExecutor = mainExecutor,
callback =
FakeCallback(
isUnlocked = isUnlocked,
@@ -36,7 +39,7 @@
object : AnimationFeatureFlags {
override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim
},
- transitionAnimator = fakeTransitionAnimator(),
+ transitionAnimator = fakeTransitionAnimator(mainExecutor),
isForTesting = true,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt
index bc7ec3f..d07875f 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeTransitionAnimator.kt
@@ -15,10 +15,12 @@
package com.android.systemui.animation
import com.android.app.animation.Interpolators
+import com.android.systemui.dagger.qualifiers.Main
+import java.util.concurrent.Executor
/** A [TransitionAnimator] to be used in tests. */
-fun fakeTransitionAnimator(): TransitionAnimator {
- return TransitionAnimator(TEST_TIMINGS, TEST_INTERPOLATORS)
+fun fakeTransitionAnimator(@Main mainExecutor: Executor): TransitionAnimator {
+ return TransitionAnimator(mainExecutor, TEST_TIMINGS, TEST_INTERPOLATORS)
}
/**
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
index 29167d6..b38acc8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorKosmos.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.notification.domain.interactor.notificationLaunchAnimationInteractor
val Kosmos.windowManagerLockscreenVisibilityInteractor by
@@ -29,5 +30,6 @@
fromBouncerInteractor = fromPrimaryBouncerTransitionInteractor,
fromAlternateBouncerInteractor = fromAlternateBouncerTransitionInteractor,
notificationLaunchAnimationInteractor = notificationLaunchAnimationInteractor,
+ sceneInteractor = sceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
index 58b0ff8..67fa857 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModelKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.accessibility.domain.interactor.accessibilityInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntrySourceInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
@@ -49,6 +50,7 @@
keyguardViewController = { statusBarKeyguardViewManager },
deviceEntryInteractor = deviceEntryInteractor,
deviceEntrySourceInteractor = deviceEntrySourceInteractor,
+ accessibilityInteractor = accessibilityInteractor,
scope = testScope.backgroundScope,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
index 38ede44..ea02d0c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ShadeTestUtil.kt
@@ -76,6 +76,16 @@
delegate.assertFlagValid()
delegate.programmaticCollapseShade()
}
+
+ fun setQsFullscreen(qsFullscreen: Boolean) {
+ delegate.assertFlagValid()
+ delegate.setQsFullscreen(qsFullscreen)
+ }
+
+ fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean) {
+ delegate.assertFlagValid()
+ delegate.setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer)
+ }
}
/** Sets up shade state for tests for a specific value of the scene container flag. */
@@ -103,6 +113,10 @@
/** Sets the shade to half collapsed with no touch input. */
fun programmaticCollapseShade()
+
+ fun setQsFullscreen(qsFullscreen: Boolean)
+
+ fun setLegacyExpandedOrAwaitingInputTransfer(legacyExpandedOrAwaitingInputTransfer: Boolean)
}
/** Sets up shade state for tests when the scene container flag is disabled. */
@@ -146,6 +160,14 @@
shadeRepository.setLegacyShadeExpansion(.5f)
testScope.runCurrent()
}
+
+ override fun setQsFullscreen(qsFullscreen: Boolean) {
+ shadeRepository.legacyQsFullscreen.value = true
+ }
+
+ override fun setLegacyExpandedOrAwaitingInputTransfer(expanded: Boolean) {
+ shadeRepository.setLegacyExpandedOrAwaitingInputTransfer(expanded)
+ }
}
/** Sets up shade state for tests when the scene container flag is enabled. */
@@ -183,6 +205,16 @@
setTransitionProgress(Scenes.Shade, Scenes.Lockscreen, .5f, false)
}
+ override fun setQsFullscreen(qsFullscreen: Boolean) {
+ setQsExpansion(1f)
+ }
+
+ override fun setLegacyExpandedOrAwaitingInputTransfer(
+ legacyExpandedOrAwaitingInputTransfer: Boolean
+ ) {
+ setShadeExpansion(.1f)
+ }
+
override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
if (lockscreenShadeExpansion == 0f) {
setIdleScene(Scenes.Lockscreen)
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 4e02470..483d308 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -859,7 +859,6 @@
break;
case android.provider.Settings.System.SCREEN_BRIGHTNESS:
- case android.provider.Settings.System.SCREEN_BRIGHTNESS_FLOAT:
case android.provider.Settings.System.SCREEN_BRIGHTNESS_MODE:
if (callingUid == Process.SYSTEM_UID) {
return false;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 25885ed..e8faff6 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -96,6 +96,7 @@
interface TransactionReadyListener {
void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
default void onTransactionCommitTimeout() {}
+ default void onReadyTimeout() {}
}
/**
@@ -410,6 +411,7 @@
if (allFinished && !mReady) {
Slog.w(TAG, "Sync group " + mSyncId + " timed-out because not ready. If you see "
+ "this, please file a bug.");
+ mListener.onReadyTimeout();
}
finishNow();
removeFromDependencies(this);
diff --git a/services/core/java/com/android/server/wm/InputConfigAdapter.java b/services/core/java/com/android/server/wm/InputConfigAdapter.java
index 119fafd..ae6e724 100644
--- a/services/core/java/com/android/server/wm/InputConfigAdapter.java
+++ b/services/core/java/com/android/server/wm/InputConfigAdapter.java
@@ -20,8 +20,6 @@
import android.view.InputWindowHandle.InputConfigFlags;
import android.view.WindowManager.LayoutParams;
-import java.util.List;
-
/**
* A helper to determine the {@link InputConfigFlags} that control the behavior of an input window
* from several WM attributes.
@@ -47,7 +45,7 @@
* input configurations that can be mapped directly from a corresponding LayoutParams input
* feature.
*/
- private static final List<FlagMapping> INPUT_FEATURE_TO_CONFIG_MAP = List.of(
+ private static final FlagMapping[] INPUT_FEATURE_TO_CONFIG_MAP = {
new FlagMapping(
LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL,
InputConfig.NO_INPUT_CHANNEL, false /* inverted */),
@@ -59,7 +57,8 @@
InputConfig.SPY, false /* inverted */),
new FlagMapping(
LayoutParams.INPUT_FEATURE_SENSITIVE_FOR_PRIVACY,
- InputConfig.SENSITIVE_FOR_PRIVACY, false /* inverted */));
+ InputConfig.SENSITIVE_FOR_PRIVACY, false /* inverted */)
+ };
@InputConfigFlags
private static final int INPUT_FEATURE_TO_CONFIG_MASK =
@@ -72,7 +71,7 @@
* NOTE: The layout params flag {@link LayoutParams#FLAG_NOT_FOCUSABLE} is not handled by this
* adapter, and must be handled explicitly.
*/
- private static final List<FlagMapping> LAYOUT_PARAM_FLAG_TO_CONFIG_MAP = List.of(
+ private static final FlagMapping[] LAYOUT_PARAM_FLAG_TO_CONFIG_MAP = {
new FlagMapping(
LayoutParams.FLAG_NOT_TOUCHABLE,
InputConfig.NOT_TOUCHABLE, false /* inverted */),
@@ -84,7 +83,8 @@
InputConfig.WATCH_OUTSIDE_TOUCH, false /* inverted */),
new FlagMapping(
LayoutParams.FLAG_SLIPPERY,
- InputConfig.SLIPPERY, false /* inverted */));
+ InputConfig.SLIPPERY, false /* inverted */)
+ };
@InputConfigFlags
private static final int LAYOUT_PARAM_FLAG_TO_CONFIG_MASK =
@@ -119,7 +119,7 @@
}
@InputConfigFlags
- private static int applyMapping(int flags, List<FlagMapping> flagToConfigMap) {
+ private static int applyMapping(int flags, FlagMapping[] flagToConfigMap) {
int inputConfig = 0;
for (final FlagMapping mapping : flagToConfigMap) {
final boolean flagSet = (flags & mapping.mFlag) != 0;
@@ -131,7 +131,7 @@
}
@InputConfigFlags
- private static int computeMask(List<FlagMapping> flagToConfigMap) {
+ private static int computeMask(FlagMapping[] flagToConfigMap) {
int mask = 0;
for (final FlagMapping mapping : flagToConfigMap) {
mask |= mapping.mInputConfig;
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 1543263..7ec31d5 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1648,14 +1648,6 @@
}
if (mController.useFullReadyTracking()) {
- if (mReadyTracker.mMet.isEmpty()) {
- Slog.e(TAG, "#" + mSyncId + ": No conditions provided");
- } else {
- for (int i = 0; i < mReadyTracker.mConditions.size(); ++i) {
- Slog.e(TAG, "#" + mSyncId + ": unmet condition at ready: "
- + mReadyTracker.mConditions.get(i));
- }
- }
for (int i = 0; i < mReadyTracker.mMet.size(); ++i) {
ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "#%d: Met condition: %s",
mSyncId, mReadyTracker.mMet.get(i));
@@ -3360,6 +3352,18 @@
applyReady();
}
+ @Override
+ public void onReadyTimeout() {
+ if (!mController.useFullReadyTracking()) {
+ Slog.e(TAG, "#" + mSyncId + " readiness timeout, used=" + mReadyTrackerOld.mUsed
+ + " deferReadyDepth=" + mReadyTrackerOld.mDeferReadyDepth
+ + " group=" + mReadyTrackerOld.mReadyGroups);
+ return;
+ }
+ Slog.e(TAG, "#" + mSyncId + " met conditions: " + mReadyTracker.mMet);
+ Slog.e(TAG, "#" + mSyncId + " unmet conditions: " + mReadyTracker.mConditions);
+ }
+
/**
* Represents a condition that must be met before an associated transition can be considered
* ready.
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 375fc5a..9eb7b22 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21605,12 +21605,9 @@
== HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER;
}
- if (Flags.headlessSingleMinTargetSdk()
- && mInjector.userManagerIsHeadlessSystemUserMode()
- && isSingleUserMode
- && !mInjector.isChangeEnabled(
- PROVISION_SINGLE_USER_MODE, deviceAdmin.getPackageName(),
- caller.getUserId())) {
+ if (Flags.headlessSingleUserFixes() && mInjector.userManagerIsHeadlessSystemUserMode()
+ && isSingleUserMode && !mInjector.isChangeEnabled(
+ PROVISION_SINGLE_USER_MODE, deviceAdmin.getPackageName(), caller.getUserId())) {
throw new IllegalStateException("Device admin is not targeting Android V.");
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index b155829..6499556 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -2046,8 +2046,20 @@
writer.println("Unknown app ID $appId.")
}
}
+ } else if (args[0] == "--package" && args.size == 2) {
+ val packageName = args[1]
+ service.getState {
+ val packageState = state.externalState.packageStates[packageName]
+ if (packageState != null) {
+ writer.dumpAppIdState(packageState.appId, state, indexedSetOf(packageName))
+ } else {
+ writer.println("Unknown package $packageName.")
+ }
+ }
} else {
- writer.println("Usage: dumpsys permission [--app-id APP_ID]")
+ writer.println(
+ "Usage: dumpsys permissionmgr [--app-id <APP_ID>] [--package <PACKAGE_NAME>]"
+ )
}
}