summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ServiceStartArgs.java2
-rw-r--r--core/java/android/app/WallpaperInfo.java8
-rw-r--r--core/java/android/view/SurfaceControl.java14
-rw-r--r--core/java/android/view/SurfaceControlViewHost.java3
-rw-r--r--core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java4
-rw-r--r--core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java12
-rw-r--r--graphics/java/android/graphics/GraphicBuffer.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java13
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt2
-rw-r--r--media/jni/android_media_ImageReader.cpp1
-rw-r--r--packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt63
-rw-r--r--packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt4
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt46
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt10
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt187
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt117
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt52
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java13
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt19
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt11
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java18
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt81
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt14
-rw-r--r--packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java6
-rw-r--r--services/core/java/com/android/server/am/UserController.java48
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java4
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java2
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java9
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java16
-rw-r--r--services/core/java/com/android/server/wm/KeyguardController.java40
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java5
-rw-r--r--services/core/java/com/android/server/wm/Transition.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java50
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt2
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt2
70 files changed, 1007 insertions, 198 deletions
diff --git a/core/java/android/app/ServiceStartArgs.java b/core/java/android/app/ServiceStartArgs.java
index 0b000af56400..9c52367392a5 100644
--- a/core/java/android/app/ServiceStartArgs.java
+++ b/core/java/android/app/ServiceStartArgs.java
@@ -49,7 +49,7 @@ public class ServiceStartArgs implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeInt(taskRemoved ? 1 : 0);
out.writeInt(startId);
- out.writeInt(flags);
+ out.writeInt(this.flags);
if (args != null) {
out.writeInt(1);
args.writeToParcel(out, 0);
diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java
index 99d406446dae..b29e73a717f4 100644
--- a/core/java/android/app/WallpaperInfo.java
+++ b/core/java/android/app/WallpaperInfo.java
@@ -292,12 +292,12 @@ public final class WallpaperInfo implements Parcelable {
packageName = mService.serviceInfo.packageName;
applicationInfo = mService.serviceInfo.applicationInfo;
}
- String contextUriString = pm.getText(
- packageName, mContextUriResource, applicationInfo).toString();
- if (contextUriString == null) {
+ CharSequence contextUriCharSequence = pm.getText(
+ packageName, mContextUriResource, applicationInfo);
+ if (contextUriCharSequence == null) {
return null;
}
- return Uri.parse(contextUriString);
+ return Uri.parse(contextUriCharSequence.toString());
}
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ddaa71c6b1b5..c11f4975149d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -786,7 +786,11 @@ public final class SurfaceControl implements Parcelable {
mReleaseStack = null;
}
setUnreleasedWarningCallSite(callsite);
- addToRegistry();
+ if (nativeObject != 0) {
+ // Only add valid surface controls to the registry. This is called at the end of this
+ // method since its information is dumped if the process threshold is reached.
+ addToRegistry();
+ }
}
/**
@@ -893,6 +897,10 @@ public final class SurfaceControl implements Parcelable {
"Only buffer layers can set a valid buffer size.");
}
+ if (mName == null) {
+ Log.w(TAG, "Missing name for SurfaceControl", new Throwable());
+ }
+
if ((mFlags & FX_SURFACE_MASK) == FX_SURFACE_NORMAL) {
setBLASTLayer();
}
@@ -1254,6 +1262,9 @@ public final class SurfaceControl implements Parcelable {
}
/**
+ * Note: Most callers should use {@link SurfaceControl.Builder} or one of the other constructors
+ * to build an instance of a SurfaceControl. This constructor is mainly used for
+ * unparceling and passing into an AIDL call as an out parameter.
* @hide
*/
public SurfaceControl() {
@@ -2495,6 +2506,7 @@ public final class SurfaceControl implements Parcelable {
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
SurfaceControl sc = new SurfaceControl();
+ sc.mName = mirrorOf.mName + " (mirror)";
sc.assignNativeObject(nativeObj, "mirrorSurface");
return sc;
}
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index d9872174f9be..c8cf7d9a5194 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -171,8 +171,7 @@ public class SurfaceControlViewHost {
public SurfacePackage(@NonNull SurfacePackage other) {
SurfaceControl otherSurfaceControl = other.mSurfaceControl;
if (otherSurfaceControl != null && otherSurfaceControl.isValid()) {
- mSurfaceControl = new SurfaceControl();
- mSurfaceControl.copyFrom(otherSurfaceControl, "SurfacePackage");
+ mSurfaceControl = new SurfaceControl(otherSurfaceControl, "SurfacePackage");
}
mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection;
mInputToken = other.mInputToken;
diff --git a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
index 6e8e93a8c86b..b2e42ba90152 100644
--- a/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
+++ b/core/tests/coretests/src/android/content/res/FontScaleConverterActivityTest.java
@@ -26,6 +26,7 @@ import android.app.Activity;
import android.compat.testing.PlatformCompatChangeRule;
import android.os.Bundle;
import android.platform.test.annotations.IwTest;
+import android.platform.test.annotations.PlatinumTest;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.util.PollingCheck;
@@ -72,6 +73,7 @@ public class FontScaleConverterActivityTest {
restoreSystemFontScaleToDefault();
}
+ @PlatinumTest(focusArea = "accessibility")
@IwTest(focusArea = "accessibility")
@Test
public void testFontsScaleNonLinearly() {
@@ -103,6 +105,7 @@ public class FontScaleConverterActivityTest {
)));
}
+ @PlatinumTest(focusArea = "accessibility")
@IwTest(focusArea = "accessibility")
@Test
public void testOnConfigurationChanged_doesNotCrash() {
@@ -117,6 +120,7 @@ public class FontScaleConverterActivityTest {
});
}
+ @PlatinumTest(focusArea = "accessibility")
@IwTest(focusArea = "accessibility")
@Test
public void testUpdateConfiguration_doesNotCrash() {
diff --git a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
index d10ba7ccbac4..e117051ba9de 100644
--- a/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
+++ b/core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java
@@ -104,6 +104,18 @@ public class SurfaceControlRegistryTests {
}
@Test
+ public void testInvalidSurfaceControlNotAddedToRegistry() {
+ int hash0 = SurfaceControlRegistry.getProcessInstance().hashCode();
+ // Verify no changes to the registry when dealing with invalid surface controls
+ SurfaceControl sc0 = new SurfaceControl();
+ SurfaceControl sc1 = new SurfaceControl(sc0, "test");
+ assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode());
+ sc0.release();
+ sc1.release();
+ assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode());
+ }
+
+ @Test
public void testThresholds() {
SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance();
TestReporter reporter = new TestReporter();
diff --git a/graphics/java/android/graphics/GraphicBuffer.java b/graphics/java/android/graphics/GraphicBuffer.java
index f9113a21405c..6705b25ab0ec 100644
--- a/graphics/java/android/graphics/GraphicBuffer.java
+++ b/graphics/java/android/graphics/GraphicBuffer.java
@@ -57,7 +57,7 @@ public class GraphicBuffer implements Parcelable {
private final int mUsage;
// Note: do not rename, this field is used by native code
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- private final long mNativeObject;
+ private long mNativeObject;
// These two fields are only used by lock/unlockCanvas()
private Canvas mCanvas;
@@ -219,6 +219,7 @@ public class GraphicBuffer implements Parcelable {
if (!mDestroyed) {
mDestroyed = true;
nDestroyGraphicBuffer(mNativeObject);
+ mNativeObject = 0;
}
}
@@ -239,7 +240,7 @@ public class GraphicBuffer implements Parcelable {
@Override
protected void finalize() throws Throwable {
try {
- if (!mDestroyed) nDestroyGraphicBuffer(mNativeObject);
+ destroy();
} finally {
super.finalize();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
index 19d8384ace41..fc301b6993c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/TransitionAnimationHelper.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
+import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -150,6 +151,10 @@ public class TransitionAnimationHelper {
.loadAnimationAttr(options.getPackageName(), options.getAnimations(),
animAttr, translucent);
}
+ } else if (translucent && !isTask && ((changeFlags & FLAGS_IS_NON_APP_WINDOW) == 0)) {
+ // Un-styled translucent activities technically have undefined animations; however,
+ // as is always the case, some apps now rely on this being no-animation, so skip
+ // loading animations here.
} else {
a = transitionAnimation.loadDefaultAnimationAttr(animAttr, translucent);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 3b306e793640..e6d4603db10d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -16,9 +16,12 @@
package com.android.wm.shell.transition;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_SLEEP;
import static android.view.WindowManager.TRANSIT_TO_BACK;
@@ -1081,6 +1084,16 @@ public class Transitions implements RemoteCallable<Transitions>,
}
}
}
+ if (request.getType() == TRANSIT_KEYGUARD_OCCLUDE && request.getTriggerTask() != null
+ && request.getTriggerTask().getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ // This freeform task is on top of keyguard, so its windowing mode should be changed to
+ // fullscreen.
+ if (wct == null) {
+ wct = new WindowContainerTransaction();
+ }
+ wct.setWindowingMode(request.getTriggerTask().token, WINDOWING_MODE_FULLSCREEN);
+ wct.setBounds(request.getTriggerTask().token, null);
+ }
mOrganizer.startTransition(transitionToken, wct != null && wct.isEmpty() ? null : wct);
active.mToken = transitionToken;
// Currently, WMCore only does one transition at a time. If it makes a requestStart, it
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index b7e73ad11c9f..72f25f36c9d9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.EdgeExtensionComponentMatcher
@@ -60,6 +61,7 @@ class CopyContentInSplit(override val flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index 0b0a3dad320b..ed3df9ced3b0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -58,6 +59,7 @@ class DragDividerToResize(override val flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index 8cf871f88314..b4c6afd14a90 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
@@ -59,6 +60,7 @@ class SwitchAppByDoubleTapDivider(override val flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
index 3b2da8dbcf9f..2cedc3570cfd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBetweenSplitPairs.kt
@@ -18,6 +18,7 @@ package com.android.wm.shell.flicker.splitscreen
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -62,6 +63,7 @@ class SwitchBetweenSplitPairs(override val flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index a189a3f67eca..a5ad97d8ad65 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -62,6 +63,7 @@ open class CopyContentInSplitBenchmark(override val flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 55ab7b3a30e4..fa6a4bfbcda6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -68,6 +69,7 @@ open class DismissSplitScreenByDividerBenchmark(flicker: FlickerTest) : SplitScr
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index c4cfd1add25c..d2beb678a4d5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -55,6 +56,7 @@ open class DismissSplitScreenByGoHomeBenchmark(override val flicker: FlickerTest
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index 146287c21c75..e95fd947ebde 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -58,6 +59,7 @@ open class DragDividerToResizeBenchmark(override val flicker: FlickerTest) :
Assume.assumeTrue(tapl.isTablet || !flicker.scenario.isLandscapeOrSeascapeAtStart)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index cc715021adf4..63b74e29c39c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -70,6 +71,7 @@ open class EnterSplitScreenByDragFromAllAppsBenchmark(override val flicker: Flic
Assume.assumeTrue(tapl.isTablet)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index de78f09d3ef4..e94da8713b43 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -67,6 +68,7 @@ open class EnterSplitScreenByDragFromNotificationBenchmark(override val flicker:
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index a29eb4085e54..f41117f0d54e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -70,6 +71,7 @@ open class EnterSplitScreenByDragFromShortcutBenchmark(flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index b2395cafafc1..12f610b73e13 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -65,6 +66,7 @@ open class EnterSplitScreenByDragFromTaskbarBenchmark(override val flicker: Flic
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index e1d85d0a4371..77818d380053 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -64,6 +65,7 @@ open class EnterSplitScreenFromOverviewBenchmark(override val flicker: FlickerTe
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index ba8c46091808..6ff22902667c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
@@ -134,6 +135,7 @@ open class SwitchAppByDoubleTapDividerBenchmark(override val flicker: FlickerTes
return displayBounds.width > displayBounds.height
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index bbb2edc621e2..400adea8880d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -63,6 +64,7 @@ open class SwitchBackToSplitFromAnotherAppBenchmark(override val flicker: Flicke
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index fa382932eb88..1ec43405ee44 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -61,6 +62,7 @@ open class SwitchBackToSplitFromHomeBenchmark(override val flicker: FlickerTest)
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index 1064bd93e3c1..9757153929ca 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -61,6 +62,7 @@ open class SwitchBackToSplitFromRecentBenchmark(override val flicker: FlickerTes
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui")
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 8f4393f8d20d..c19a38dc9daf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.splitscreen.benchmark
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
@@ -64,6 +65,7 @@ open class SwitchBetweenSplitPairsBenchmark(override val flicker: FlickerTest) :
thisTransition(this)
}
+ @PlatinumTest(focusArea = "sysui")
@IwTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {}
companion object {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index ca1bb3e97815..da2e56f5b6fa 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -768,6 +768,7 @@ static void ImageReader_unlockGraphicBuffer(JNIEnv* env, jobject /*thiz*/,
android_graphics_GraphicBuffer_getNativeGraphicsBuffer(env, buffer);
if (graphicBuffer.get() == NULL) {
jniThrowRuntimeException(env, "Invalid graphic buffer!");
+ return;
}
status_t res = graphicBuffer->unlock();
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
new file mode 100644
index 000000000000..8fe1f48dcaee
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Easings.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.animation
+
+import androidx.compose.animation.core.Easing
+import androidx.core.animation.Interpolator
+import com.android.app.animation.InterpolatorsAndroidX
+
+/**
+ * Compose-compatible definition of Android motion eases, see
+ * https://carbon.googleplex.com/android-motion/pages/easing
+ */
+object Easings {
+
+ /** The standard interpolator that should be used on every normal animation */
+ val StandardEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD)
+
+ /**
+ * The standard accelerating interpolator that should be used on every regular movement of
+ * content that is disappearing e.g. when moving off screen.
+ */
+ val StandardAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_ACCELERATE)
+
+ /**
+ * The standard decelerating interpolator that should be used on every regular movement of
+ * content that is appearing e.g. when coming from off screen.
+ */
+ val StandardDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.STANDARD_DECELERATE)
+
+ /** The default emphasized interpolator. Used for hero / emphasized movement of content. */
+ val EmphasizedEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED)
+
+ /**
+ * The accelerated emphasized interpolator. Used for hero / emphasized movement of content that
+ * is disappearing e.g. when moving off screen.
+ */
+ val EmphasizedAccelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_ACCELERATE)
+
+ /**
+ * The decelerating emphasized interpolator. Used for hero / emphasized movement of content that
+ * is appearing e.g. when coming from off screen
+ */
+ val EmphasizedDecelerateEasing = fromInterpolator(InterpolatorsAndroidX.EMPHASIZED_DECELERATE)
+
+ /** The linear interpolator. */
+ val LinearEasing = fromInterpolator(InterpolatorsAndroidX.LINEAR)
+
+ private fun fromInterpolator(source: Interpolator) = Easing { x -> source.getInterpolation(x) }
+}
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
index 954bad56bcc2..d3643747ad91 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/ui/composable/SceneModule.kt
@@ -16,6 +16,7 @@
package com.android.systemui.scene.ui.composable
+import android.content.Context
import com.android.systemui.bouncer.ui.composable.BouncerScene
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.dagger.SysUISingleton
@@ -28,6 +29,7 @@ import com.android.systemui.scene.shared.model.Scene
import com.android.systemui.scene.shared.model.SceneContainerNames
import com.android.systemui.shade.ui.composable.ShadeScene
import com.android.systemui.shade.ui.viewmodel.ShadeSceneViewModel
+import com.android.systemui.statusbar.phone.SystemUIDialog
import dagger.Module
import dagger.Provides
import javax.inject.Named
@@ -57,6 +59,7 @@ object SceneModule {
@SysUISingleton
@Named(SceneContainerNames.SYSTEM_UI_DEFAULT)
fun bouncerScene(
+ @Application context: Context,
viewModelFactory: BouncerViewModel.Factory,
): BouncerScene {
return BouncerScene(
@@ -64,6 +67,7 @@ object SceneModule {
viewModelFactory.create(
containerName = SceneContainerNames.SYSTEM_UI_DEFAULT,
),
+ dialogFactory = { SystemUIDialog(context) },
)
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index 3c74ef5adfeb..240bace21a1c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -14,9 +14,16 @@
* limitations under the License.
*/
+@file:OptIn(ExperimentalMaterial3Api::class)
+
package com.android.systemui.bouncer.ui.composable
+import android.app.AlertDialog
+import android.app.Dialog
+import android.content.DialogInterface
import androidx.compose.animation.Crossfade
+import androidx.compose.animation.core.snap
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
@@ -26,15 +33,20 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
+import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.AuthMethodBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PasswordBouncerViewModel
@@ -51,6 +63,7 @@ import kotlinx.coroutines.flow.asStateFlow
/** The bouncer scene displays authentication challenges like PIN, password, or pattern. */
class BouncerScene(
private val viewModel: BouncerViewModel,
+ private val dialogFactory: () -> AlertDialog,
) : ComposableScene {
override val key = SceneKey.Bouncer
@@ -68,16 +81,19 @@ class BouncerScene(
override fun Content(
containerName: String,
modifier: Modifier,
- ) = BouncerScene(viewModel, modifier)
+ ) = BouncerScene(viewModel, dialogFactory, modifier)
}
@Composable
private fun BouncerScene(
viewModel: BouncerViewModel,
+ dialogFactory: () -> AlertDialog,
modifier: Modifier = Modifier,
) {
- val message: String by viewModel.message.collectAsState()
+ val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
val authMethodViewModel: AuthMethodBouncerViewModel? by viewModel.authMethod.collectAsState()
+ val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
+ var dialog: Dialog? by remember { mutableStateOf(null) }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -88,9 +104,10 @@ private fun BouncerScene(
Crossfade(
targetState = message,
label = "Bouncer message",
- ) {
+ animationSpec = if (message.isUpdateAnimated) tween() else snap(),
+ ) { message ->
Text(
- text = it,
+ text = message.text,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.bodyLarge,
)
@@ -132,5 +149,26 @@ private fun BouncerScene(
style = MaterialTheme.typography.bodyMedium,
)
}
+
+ if (dialogMessage != null) {
+ if (dialog == null) {
+ dialog =
+ dialogFactory().apply {
+ setMessage(dialogMessage)
+ setButton(
+ DialogInterface.BUTTON_NEUTRAL,
+ context.getString(R.string.ok),
+ ) { _, _ ->
+ viewModel.onThrottlingDialogDismissed()
+ }
+ setCancelable(false)
+ setCanceledOnTouchOutside(false)
+ show()
+ }
+ }
+ } else {
+ dialog?.dismiss()
+ dialog = null
+ }
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index 4e85621e9e23..7545ff464bab 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -53,6 +53,8 @@ internal fun PasswordBouncer(
) {
val focusRequester = remember { FocusRequester() }
val password: String by viewModel.password.collectAsState()
+ val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
+ val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
LaunchedEffect(Unit) {
// When the UI comes up, request focus on the TextField to bring up the software keyboard.
@@ -61,6 +63,13 @@ internal fun PasswordBouncer(
viewModel.onShown()
}
+ LaunchedEffect(animateFailure) {
+ if (animateFailure) {
+ // We don't currently have a failure animation for password, just consume it:
+ viewModel.onFailureAnimationShown()
+ }
+ }
+
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier,
@@ -71,6 +80,7 @@ internal fun PasswordBouncer(
TextField(
value = password,
onValueChange = viewModel::onPasswordInputChanged,
+ enabled = isInputEnabled,
visualTransformation = PasswordVisualTransformation(),
singleLine = true,
textStyle = LocalTextStyle.current.copy(textAlign = TextAlign.Center),
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index 3afd33f4c90c..88441146ad23 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -18,6 +18,7 @@ package com.android.systemui.bouncer.ui.composable
import android.view.HapticFeedbackConstants
import androidx.compose.animation.core.Animatable
+import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.gestures.detectDragGestures
@@ -41,12 +42,15 @@ import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.integerResource
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Easings
import com.android.internal.R
import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel
+import com.android.systemui.compose.modifiers.thenIf
import kotlin.math.min
import kotlin.math.pow
import kotlin.math.sqrt
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
/**
@@ -66,9 +70,9 @@ internal fun PatternBouncer(
val rowCount = viewModel.rowCount
val dotColor = MaterialTheme.colorScheme.secondary
- val dotRadius = with(LocalDensity.current) { 8.dp.toPx() }
+ val dotRadius = with(LocalDensity.current) { (DOT_DIAMETER_DP / 2).dp.toPx() }
val lineColor = MaterialTheme.colorScheme.primary
- val lineStrokeWidth = dotRadius * 2 + with(LocalDensity.current) { 4.dp.toPx() }
+ val lineStrokeWidth = with(LocalDensity.current) { LINE_STROKE_WIDTH_DP.dp.toPx() }
var containerSize: IntSize by remember { mutableStateOf(IntSize(0, 0)) }
val horizontalSpacing = containerSize.width / colCount
@@ -82,6 +86,9 @@ internal fun PatternBouncer(
val currentDot: PatternDotViewModel? by viewModel.currentDot.collectAsState()
// The dots selected so far, if the user is currently dragging.
val selectedDots: List<PatternDotViewModel> by viewModel.selectedDots.collectAsState()
+ val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
+ val isAnimationEnabled: Boolean by viewModel.isPatternVisible.collectAsState()
+ val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
// Map of animatables for the scale of each dot, keyed by dot.
val dotScalingAnimatables = remember(dots) { dots.associateWith { Animatable(1f) } }
@@ -96,19 +103,46 @@ internal fun PatternBouncer(
val view = LocalView.current
// When the current dot is changed, we need to update our animations.
- LaunchedEffect(currentDot) {
- view.performHapticFeedback(
- HapticFeedbackConstants.VIRTUAL_KEY,
- HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
- )
+ LaunchedEffect(currentDot, isAnimationEnabled) {
+ // Perform haptic feedback, but only if the current dot is not null, so we don't perform it
+ // when the UI first shows up or when the user lifts their pointer/finger.
+ if (currentDot != null) {
+ view.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+ )
+ }
+
+ if (!isAnimationEnabled) {
+ return@LaunchedEffect
+ }
- // Make sure that the current dot is scaled up while the other dots are scaled back down.
+ // Make sure that the current dot is scaled up while the other dots are scaled back
+ // down.
dotScalingAnimatables.entries.forEach { (dot, animatable) ->
val isSelected = dot == currentDot
- launch {
- animatable.animateTo(if (isSelected) 2f else 1f)
+ // Launch using the longer-lived scope because we want these animations to proceed to
+ // completion even if the LaunchedEffect is canceled because its key objects have
+ // changed.
+ scope.launch {
if (isSelected) {
- animatable.animateTo(1f)
+ animatable.animateTo(
+ targetValue = (SELECTED_DOT_DIAMETER_DP / DOT_DIAMETER_DP.toFloat()),
+ animationSpec =
+ tween(
+ durationMillis = SELECTED_DOT_REACTION_ANIMATION_DURATION_MS,
+ easing = Easings.StandardAccelerateEasing,
+ ),
+ )
+ } else {
+ animatable.animateTo(
+ targetValue = 1f,
+ animationSpec =
+ tween(
+ durationMillis = SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS,
+ easing = Easings.StandardDecelerateEasing,
+ ),
+ )
}
}
}
@@ -116,14 +150,18 @@ internal fun PatternBouncer(
selectedDots.forEach { dot ->
lineFadeOutAnimatables[dot]?.let { line ->
if (!line.isRunning) {
+ // Launch using the longer-lived scope because we want these animations to
+ // proceed to completion even if the LaunchedEffect is canceled because its key
+ // objects have changed.
scope.launch {
if (dot == currentDot) {
- // Reset the fade-out animation for the current dot. When the current
- // dot is switched, this entire code block runs again for the newly
- // selected dot.
+ // Reset the fade-out animation for the current dot. When the
+ // current dot is switched, this entire code block runs again for
+ // the newly selected dot.
line.snapTo(1f)
} else {
- // For all non-current dots, make sure that the lines are fading out.
+ // For all non-current dots, make sure that the lines are fading
+ // out.
line.animateTo(
targetValue = 0f,
animationSpec =
@@ -139,6 +177,17 @@ internal fun PatternBouncer(
}
}
+ // Show the failure animation if the user entered the wrong input.
+ LaunchedEffect(animateFailure) {
+ if (animateFailure) {
+ showFailureAnimation(
+ dots = dots,
+ scalingAnimatables = dotScalingAnimatables,
+ )
+ viewModel.onFailureAnimationShown()
+ }
+ }
+
// This is the position of the input pointer.
var inputPosition: Offset? by remember { mutableStateOf(null) }
@@ -148,27 +197,34 @@ internal fun PatternBouncer(
// when it leaves the bounds of the dot grid.
.clipToBounds()
.onSizeChanged { containerSize = it }
- .pointerInput(Unit) {
- detectDragGestures(
- onDragStart = { start ->
- inputPosition = start
- viewModel.onDragStart()
- },
- onDragEnd = {
- inputPosition = null
- lineFadeOutAnimatables.values.forEach { animatable ->
- scope.launch { animatable.animateTo(1f) }
- }
- viewModel.onDragEnd()
- },
- ) { change, _ ->
- inputPosition = change.position
- viewModel.onDrag(
- xPx = change.position.x,
- yPx = change.position.y,
- containerSizePx = containerSize.width,
- verticalOffsetPx = verticalOffset,
- )
+ .thenIf(isInputEnabled) {
+ Modifier.pointerInput(Unit) {
+ detectDragGestures(
+ onDragStart = { start ->
+ inputPosition = start
+ viewModel.onDragStart()
+ },
+ onDragEnd = {
+ inputPosition = null
+ if (isAnimationEnabled) {
+ lineFadeOutAnimatables.values.forEach { animatable ->
+ // Launch using the longer-lived scope because we want these
+ // animations to proceed to completion even if the surrounding
+ // scope is canceled.
+ scope.launch { animatable.animateTo(1f) }
+ }
+ }
+ viewModel.onDragEnd()
+ },
+ ) { change, _ ->
+ inputPosition = change.position
+ viewModel.onDrag(
+ xPx = change.position.x,
+ yPx = change.position.y,
+ containerSizePx = containerSize.width,
+ verticalOffsetPx = verticalOffset,
+ )
+ }
}
}
) {
@@ -247,3 +303,62 @@ private fun lineAlpha(gridSpacing: Float, lineLength: Float = gridSpacing): Floa
// farther the user input pointer goes from the line, the more opaque the line gets.
return ((lineLength / gridSpacing - 0.3f) * 4f).coerceIn(0f, 1f)
}
+
+private suspend fun showFailureAnimation(
+ dots: List<PatternDotViewModel>,
+ scalingAnimatables: Map<PatternDotViewModel, Animatable<Float, AnimationVector1D>>,
+) {
+ val dotsByRow =
+ buildList<MutableList<PatternDotViewModel>> {
+ dots.forEach { dot ->
+ val rowIndex = dot.y
+ while (size <= rowIndex) {
+ add(mutableListOf())
+ }
+ get(rowIndex).add(dot)
+ }
+ }
+
+ coroutineScope {
+ dotsByRow.forEachIndexed { rowIndex, rowDots ->
+ rowDots.forEach { dot ->
+ scalingAnimatables[dot]?.let { dotScaleAnimatable ->
+ launch {
+ dotScaleAnimatable.animateTo(
+ targetValue =
+ FAILURE_ANIMATION_DOT_DIAMETER_DP / DOT_DIAMETER_DP.toFloat(),
+ animationSpec =
+ tween(
+ durationMillis =
+ FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS,
+ delayMillis =
+ rowIndex * FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS,
+ easing = Easings.LinearEasing,
+ ),
+ )
+
+ dotScaleAnimatable.animateTo(
+ targetValue = 1f,
+ animationSpec =
+ tween(
+ durationMillis =
+ FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION,
+ easing = Easings.StandardEasing,
+ ),
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+private const val DOT_DIAMETER_DP = 16
+private const val SELECTED_DOT_DIAMETER_DP = 24
+private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
+private const val SELECTED_DOT_RETRACT_ANIMATION_DURATION_MS = 750
+private const val LINE_STROKE_WIDTH_DP = 16
+private const val FAILURE_ANIMATION_DOT_DIAMETER_DP = 13
+private const val FAILURE_ANIMATION_DOT_SHRINK_ANIMATION_DURATION_MS = 50
+private const val FAILURE_ANIMATION_DOT_SHRINK_STAGGER_DELAY_MS = 33
+private const val FAILURE_ANIMATION_DOT_REVERT_ANIMATION_DURATION = 617
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 9c210c225ab3..968e5ab8ad8c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -18,11 +18,15 @@
package com.android.systemui.bouncer.ui.composable
+import android.view.HapticFeedbackConstants
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.animateContentSize
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
@@ -48,6 +52,7 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -55,15 +60,23 @@ import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
+import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.compose.modifiers.thenIf
import kotlin.math.max
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.DurationUnit
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
@Composable
internal fun PinBouncer(
@@ -75,6 +88,16 @@ internal fun PinBouncer(
// The length of the PIN input received so far, so we know how many dots to render.
val pinLength: Pair<Int, Int> by viewModel.pinLengths.collectAsState()
+ val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
+ val animateFailure: Boolean by viewModel.animateFailure.collectAsState()
+
+ // Show the failure animation if the user entered the wrong input.
+ LaunchedEffect(animateFailure) {
+ if (animateFailure) {
+ showFailureAnimation()
+ viewModel.onFailureAnimationShown()
+ }
+ }
Column(
horizontalAlignment = Alignment.CenterHorizontally,
@@ -116,6 +139,7 @@ internal fun PinBouncer(
val digit = index + 1
PinButton(
onClicked = { viewModel.onPinButtonClicked(digit) },
+ isEnabled = isInputEnabled,
) { contentColor ->
PinDigit(digit, contentColor)
}
@@ -124,7 +148,8 @@ internal fun PinBouncer(
PinButton(
onClicked = { viewModel.onBackspaceButtonClicked() },
onLongPressed = { viewModel.onBackspaceButtonLongPressed() },
- isHighlighted = true,
+ isEnabled = isInputEnabled,
+ isIconButton = true,
) { contentColor ->
PinIcon(
Icon.Resource(
@@ -138,13 +163,15 @@ internal fun PinBouncer(
PinButton(
onClicked = { viewModel.onPinButtonClicked(0) },
+ isEnabled = isInputEnabled,
) { contentColor ->
PinDigit(0, contentColor)
}
PinButton(
onClicked = { viewModel.onAuthenticateButtonClicked() },
- isHighlighted = true,
+ isEnabled = isInputEnabled,
+ isIconButton = true,
) { contentColor ->
PinIcon(
Icon.Resource(
@@ -187,61 +214,107 @@ private fun PinIcon(
@Composable
private fun PinButton(
onClicked: () -> Unit,
+ isEnabled: Boolean,
modifier: Modifier = Modifier,
onLongPressed: (() -> Unit)? = null,
- isHighlighted: Boolean = false,
+ isIconButton: Boolean = false,
content: @Composable (contentColor: Color) -> Unit,
) {
var isPressed: Boolean by remember { mutableStateOf(false) }
+
+ val view = LocalView.current
+ LaunchedEffect(isPressed) {
+ if (isPressed) {
+ view.performHapticFeedback(
+ HapticFeedbackConstants.VIRTUAL_KEY,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING,
+ )
+ }
+ }
+
+ // Pin button animation specification is asymmetric: fast animation to the pressed state, and a
+ // slow animation upon release. Note that isPressed is guaranteed to be true for at least the
+ // press animation duration (see below in detectTapGestures).
+ val animEasing = if (isPressed) pinButtonPressedEasing else pinButtonReleasedEasing
+ val animDurationMillis =
+ (if (isPressed) pinButtonPressedDuration else pinButtonReleasedDuration).toInt(
+ DurationUnit.MILLISECONDS
+ )
+
val cornerRadius: Dp by
animateDpAsState(
- if (isPressed) 24.dp else PinButtonSize / 2,
+ if (isPressed) 24.dp else pinButtonSize / 2,
label = "PinButton round corners",
+ animationSpec = tween(animDurationMillis, easing = animEasing)
)
+ val colorAnimationSpec: AnimationSpec<Color> = tween(animDurationMillis, easing = animEasing)
val containerColor: Color by
animateColorAsState(
when {
- isPressed -> MaterialTheme.colorScheme.primaryContainer
- isHighlighted -> MaterialTheme.colorScheme.secondaryContainer
- else -> MaterialTheme.colorScheme.surface
+ isPressed -> MaterialTheme.colorScheme.primary
+ isIconButton -> MaterialTheme.colorScheme.secondaryContainer
+ else -> MaterialTheme.colorScheme.surfaceVariant
},
label = "Pin button container color",
+ animationSpec = colorAnimationSpec
)
val contentColor: Color by
animateColorAsState(
when {
- isPressed -> MaterialTheme.colorScheme.onPrimaryContainer
- isHighlighted -> MaterialTheme.colorScheme.onSecondaryContainer
- else -> MaterialTheme.colorScheme.onSurface
+ isPressed -> MaterialTheme.colorScheme.onPrimary
+ isIconButton -> MaterialTheme.colorScheme.onSecondaryContainer
+ else -> MaterialTheme.colorScheme.onSurfaceVariant
},
label = "Pin button container color",
+ animationSpec = colorAnimationSpec
)
+ val scope = rememberCoroutineScope()
+
Box(
contentAlignment = Alignment.Center,
modifier =
modifier
- .size(PinButtonSize)
+ .size(pinButtonSize)
.drawBehind {
drawRoundRect(
color = containerColor,
cornerRadius = CornerRadius(cornerRadius.toPx()),
)
}
- .pointerInput(Unit) {
- detectTapGestures(
- onPress = {
- isPressed = true
- tryAwaitRelease()
- isPressed = false
- },
- onTap = { onClicked() },
- onLongPress = onLongPressed?.let { { onLongPressed() } },
- )
+ .thenIf(isEnabled) {
+ Modifier.pointerInput(Unit) {
+ detectTapGestures(
+ onPress = {
+ scope.launch {
+ isPressed = true
+ val minDuration = async {
+ delay(pinButtonPressedDuration + pinButtonHoldTime)
+ }
+ tryAwaitRelease()
+ minDuration.await()
+ isPressed = false
+ }
+ },
+ onTap = { onClicked() },
+ onLongPress = onLongPressed?.let { { onLongPressed() } },
+ )
+ }
},
) {
content(contentColor)
}
}
-private val PinButtonSize = 84.dp
+private fun showFailureAnimation() {
+ // TODO(b/282730134): implement.
+}
+
+private val pinButtonSize = 84.dp
+
+// Pin button motion spec: http://shortn/_9TTIG6SoEa
+private val pinButtonPressedDuration = 100.milliseconds
+private val pinButtonPressedEasing = LinearEasing
+private val pinButtonHoldTime = 33.milliseconds
+private val pinButtonReleasedDuration = 420.milliseconds
+private val pinButtonReleasedEasing = Easings.StandardEasing
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt
new file mode 100644
index 000000000000..83071d78c64d
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/compose/modifiers/ConditionalModifiers.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.compose.modifiers
+
+import androidx.compose.ui.Modifier
+
+/**
+ * Concatenates this modifier with another if `condition` is true.
+ *
+ * @param condition Whether or not to apply the modifiers.
+ * @param factory Creates the modifier to concatenate with the current one.
+ * @return a Modifier representing this modifier followed by other in sequence.
+ * @see Modifier.then
+ *
+ * This method allows inline conditional addition of modifiers to a modifier chain. Instead of
+ * writing
+ *
+ * ```
+ * val aModifier = Modifier.a()
+ * val bModifier = if(condition) aModifier.b() else aModifier
+ * Composable(modifier = bModifier)
+ * ```
+ *
+ * You can instead write
+ *
+ * ```
+ * Composable(modifier = Modifier.a().thenIf(condition){
+ * Modifier.b()
+ * }
+ * ```
+ *
+ * This makes the modifier chain easier to read.
+ *
+ * Note that unlike the non-factory version, the conditional modifier is recreated each time, and
+ * may never be created at all.
+ */
+inline fun Modifier.thenIf(condition: Boolean, crossinline factory: () -> Modifier): Modifier =
+ if (condition) this.then(factory()) else this
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index afc25909ca91..99b5d52f8322 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -158,15 +158,18 @@ public class KeyguardPatternView extends KeyguardInputView
public void startAppearAnimation() {
enableClipping(false);
- setAlpha(1f);
+ setAlpha(0f);
setTranslationY(mAppearAnimationUtils.getStartTranslation());
AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
0, mAppearAnimationUtils.getInterpolator(),
getAnimationListener(InteractionJankMonitor.CUJ_LOCKSCREEN_PATTERN_APPEAR));
- mAppearAnimationUtils.startAnimation2d(
- mLockPatternView.getCellStates(),
- () -> enableClipping(true),
- this);
+ mLockPatternView.post(() -> {
+ setAlpha(1f);
+ mAppearAnimationUtils.startAnimation2d(
+ mLockPatternView.getCellStates(),
+ () -> enableClipping(true),
+ KeyguardPatternView.this);
+ });
if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index e8046dc07c02..1721891550a1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -246,6 +246,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private static final int MSG_TIME_FORMAT_UPDATE = 344;
private static final int MSG_REQUIRE_NFC_UNLOCK = 345;
private static final int MSG_KEYGUARD_DISMISS_ANIMATION_FINISHED = 346;
+ private static final int MSG_SERVICE_PROVIDERS_UPDATED = 347;
/** Biometric authentication state: Not listening. */
private static final int BIOMETRIC_STATE_STOPPED = 0;
@@ -1826,6 +1827,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
} else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state));
+ } else if (TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED.equals(action)) {
+ mHandler.obtainMessage(MSG_SERVICE_PROVIDERS_UPDATED, intent).sendToTarget();
} else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
mHandler.sendEmptyMessage(MSG_AIRPLANE_MODE_CHANGED);
} else if (Intent.ACTION_SERVICE_STATE.equals(action)) {
@@ -2402,6 +2405,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
case MSG_SERVICE_STATE_CHANGE:
handleServiceStateChange(msg.arg1, (ServiceState) msg.obj);
break;
+ case MSG_SERVICE_PROVIDERS_UPDATED:
+ handleServiceProvidersUpdated((Intent) msg.obj);
+ break;
case MSG_SCREEN_TURNED_OFF:
Trace.beginSection("KeyguardUpdateMonitor#handler MSG_SCREEN_TURNED_OFF");
handleScreenTurnedOff();
@@ -2472,6 +2478,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
filter.addAction(Intent.ACTION_SERVICE_STATE);
filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED);
+ filter.addAction(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
filter.addAction(UsbManager.ACTION_USB_PORT_COMPLIANCE_CHANGED);
mBroadcastDispatcher.registerReceiverWithHandler(mBroadcastReceiver, filter, mHandler);
@@ -3727,6 +3734,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
+ * Handle {@link #MSG_SERVICE_PROVIDERS_UPDATED}
+ */
+ private void handleServiceProvidersUpdated(Intent intent) {
+ mLogger.logServiceProvidersUpdated(intent);
+ callbacksRefreshCarrierInfo();
+ }
+
+ /**
* Whether the keyguard is showing and not occluded.
*/
public boolean isKeyguardVisible() {
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index 17cc23632d94..4923ab0fab18 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -16,11 +16,15 @@
package com.android.keyguard.logging
+import android.content.Intent
import android.hardware.biometrics.BiometricConstants.LockoutMode
import android.os.PowerManager
import android.os.PowerManager.WakeReason
import android.telephony.ServiceState
import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
+import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
+import android.telephony.TelephonyManager
import com.android.keyguard.ActiveUnlockConfig
import com.android.keyguard.FaceAuthUiEvent
import com.android.keyguard.KeyguardListenModel
@@ -363,6 +367,21 @@ constructor(@KeyguardUpdateMonitorLog private val logBuffer: LogBuffer) {
)
}
+ fun logServiceProvidersUpdated(intent: Intent) {
+ logBuffer.log(
+ TAG,
+ VERBOSE,
+ {
+ int1 = intent.getIntExtra(EXTRA_SUBSCRIPTION_INDEX, INVALID_SUBSCRIPTION_ID)
+ str1 = intent.getStringExtra(TelephonyManager.EXTRA_SPN)
+ str2 = intent.getStringExtra(TelephonyManager.EXTRA_PLMN)
+ },
+ {
+ "action SERVICE_PROVIDERS_UPDATED subId=$int1 spn=$str1 plmn=$str2"
+ }
+ )
+ }
+
fun logSimState(subId: Int, slotId: Int, state: Int) {
logBuffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index e462e2f5b7d8..1d2fce7d8b05 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -148,12 +148,17 @@ constructor(
*
* If the input is correct, the device will be unlocked and the lock screen and bouncer will be
* dismissed and hidden.
+ *
+ * @param input The input from the user to try to authenticate with. This can be a list of
+ * different things, based on the current authentication method.
+ * @return `true` if the authentication succeeded and the device is now unlocked; `false`
+ * otherwise.
*/
fun authenticate(
input: List<Any>,
- ) {
+ ): Boolean {
if (repository.throttling.value != null) {
- return
+ return false
}
val isAuthenticated = authenticationInteractor.authenticate(input)
@@ -186,6 +191,8 @@ constructor(
}
else -> repository.setMessage(errorMessage(authenticationMethod.value))
}
+
+ return isAuthenticated
}
private fun promptMessage(authMethod: AuthenticationMethodModel): String {
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
index 774a5593530c..d95b70c85fe0 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModel.kt
@@ -16,14 +16,37 @@
package com.android.systemui.bouncer.ui.viewmodel
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.asStateFlow
-sealed interface AuthMethodBouncerViewModel {
+sealed class AuthMethodBouncerViewModel(
/**
* Whether user input is enabled.
*
* If `false`, user input should be completely ignored in the UI as the user is "locked out" of
* being able to attempt to unlock the device.
*/
- val isInputEnabled: StateFlow<Boolean>
+ val isInputEnabled: StateFlow<Boolean>,
+) {
+
+ private val _animateFailure = MutableStateFlow(false)
+ /**
+ * Whether a failure animation should be shown. Once consumed, the UI must call
+ * [onFailureAnimationShown] to consume this state.
+ */
+ val animateFailure: StateFlow<Boolean> = _animateFailure.asStateFlow()
+
+ /**
+ * Notifies that the failure animation has been shown. This should be called to consume a `true`
+ * value in [animateFailure].
+ */
+ fun onFailureAnimationShown() {
+ _animateFailure.value = false
+ }
+
+ /** Ask the UI to show the failure animation. */
+ protected fun showFailureAnimation() {
+ _animateFailure.value = true
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 02991bd47c6e..984d9ab1c1be 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -20,6 +20,7 @@ import android.content.Context
import com.android.systemui.R
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
+import com.android.systemui.bouncer.shared.model.AuthenticationThrottledModel
import com.android.systemui.dagger.qualifiers.Application
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -29,6 +30,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -45,21 +47,6 @@ constructor(
) {
private val interactor: BouncerInteractor = interactorFactory.create(containerName)
- /**
- * Whether updates to the message should be cross-animated from one message to another.
- *
- * If `false`, no animation should be applied, the message text should just be replaced
- * instantly.
- */
- val isMessageUpdateAnimationsEnabled: StateFlow<Boolean> =
- interactor.throttling
- .map { it == null }
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.WhileSubscribed(),
- initialValue = interactor.throttling.value == null,
- )
-
private val isInputEnabled: StateFlow<Boolean> =
interactor.throttling
.map { it == null }
@@ -104,13 +91,21 @@ constructor(
)
/** The user-facing message to show in the bouncer. */
- val message: StateFlow<String> =
- interactor.message
- .map { it ?: "" }
+ val message: StateFlow<MessageViewModel> =
+ combine(
+ interactor.message,
+ interactor.throttling,
+ ) { message, throttling ->
+ toMessageViewModel(message, throttling)
+ }
.stateIn(
scope = applicationScope,
started = SharingStarted.WhileSubscribed(),
- initialValue = interactor.message.value ?: "",
+ initialValue =
+ toMessageViewModel(
+ message = interactor.message.value,
+ throttling = interactor.throttling.value,
+ ),
)
private val _throttlingDialogMessage = MutableStateFlow<String?>(null)
@@ -177,6 +172,28 @@ constructor(
}
}
+ private fun toMessageViewModel(
+ message: String?,
+ throttling: AuthenticationThrottledModel?,
+ ): MessageViewModel {
+ return MessageViewModel(
+ text = message ?: "",
+ isUpdateAnimated = throttling == null,
+ )
+ }
+
+ data class MessageViewModel(
+ val text: String,
+
+ /**
+ * Whether updates to the message should be cross-animated from one message to another.
+ *
+ * If `false`, no animation should be applied, the message text should just be replaced
+ * instantly.
+ */
+ val isUpdateAnimated: Boolean,
+ )
+
@AssistedFactory
interface Factory {
fun create(
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
index c38fcaa3b657..55929b566cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModel.kt
@@ -24,8 +24,11 @@ import kotlinx.coroutines.flow.asStateFlow
/** Holds UI state and handles user input for the password bouncer UI. */
class PasswordBouncerViewModel(
private val interactor: BouncerInteractor,
- override val isInputEnabled: StateFlow<Boolean>,
-) : AuthMethodBouncerViewModel {
+ isInputEnabled: StateFlow<Boolean>,
+) :
+ AuthMethodBouncerViewModel(
+ isInputEnabled = isInputEnabled,
+ ) {
private val _password = MutableStateFlow("")
/** The password entered so far. */
@@ -47,7 +50,10 @@ class PasswordBouncerViewModel(
/** Notifies that the user has pressed the key for attempting to authenticate the password. */
fun onAuthenticateKeyPressed() {
- interactor.authenticate(password.value.toCharArray().toList())
+ if (!interactor.authenticate(password.value.toCharArray().toList())) {
+ showFailureAnimation()
+ }
+
_password.value = ""
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
index 1b0b38ea6e9c..d9ef75db6103 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModel.kt
@@ -37,8 +37,11 @@ class PatternBouncerViewModel(
private val applicationContext: Context,
applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
- override val isInputEnabled: StateFlow<Boolean>,
-) : AuthMethodBouncerViewModel {
+ isInputEnabled: StateFlow<Boolean>,
+) :
+ AuthMethodBouncerViewModel(
+ isInputEnabled = isInputEnabled,
+ ) {
/** The number of columns in the dot grid. */
val columnCount = 3
@@ -150,7 +153,11 @@ class PatternBouncerViewModel(
/** Notifies that the user has ended the drag gesture across the dot grid. */
fun onDragEnd() {
- interactor.authenticate(_selectedDots.value.map { it.toCoordinate() })
+ val isSuccessfullyAuthenticated =
+ interactor.authenticate(_selectedDots.value.map { it.toCoordinate() })
+ if (!isSuccessfullyAuthenticated) {
+ showFailureAnimation()
+ }
_dots.value = defaultDots()
_currentDot.value = null
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
index 2a733d93b857..5c0fd92e7299 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModel.kt
@@ -33,8 +33,11 @@ import kotlinx.coroutines.launch
class PinBouncerViewModel(
private val applicationScope: CoroutineScope,
private val interactor: BouncerInteractor,
- override val isInputEnabled: StateFlow<Boolean>,
-) : AuthMethodBouncerViewModel {
+ isInputEnabled: StateFlow<Boolean>,
+) :
+ AuthMethodBouncerViewModel(
+ isInputEnabled = isInputEnabled,
+ ) {
private val entered = MutableStateFlow<List<Int>>(emptyList())
/**
@@ -92,7 +95,10 @@ class PinBouncerViewModel(
/** Notifies that the user clicked the "enter" button. */
fun onAuthenticateButtonClicked() {
- interactor.authenticate(entered.value)
+ if (!interactor.authenticate(entered.value)) {
+ showFailureAnimation()
+ }
+
entered.value = emptyList()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index c8d37a165a0e..a8d662c96284 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -406,19 +406,21 @@ object KeyguardBottomAreaViewBinder {
view.isClickable = viewModel.isClickable
if (viewModel.isClickable) {
if (viewModel.useLongPress) {
- view.setOnTouchListener(
- KeyguardQuickAffordanceOnTouchListener(
- view,
- viewModel,
- messageDisplayer,
- vibratorHelper,
- falsingManager,
- )
+ val onTouchListener = KeyguardQuickAffordanceOnTouchListener(
+ view,
+ viewModel,
+ messageDisplayer,
+ vibratorHelper,
+ falsingManager,
)
+ view.setOnTouchListener(onTouchListener)
+ view.onLongClickListener =
+ OnLongClickListener(falsingManager, viewModel, vibratorHelper, onTouchListener)
} else {
view.setOnClickListener(OnClickListener(viewModel, checkNotNull(falsingManager)))
}
} else {
+ view.onLongClickListener = null
view.setOnClickListener(null)
view.setOnTouchListener(null)
}
@@ -454,6 +456,42 @@ object KeyguardBottomAreaViewBinder {
.start()
}
+ private class OnLongClickListener(
+ private val falsingManager: FalsingManager?,
+ private val viewModel: KeyguardQuickAffordanceViewModel,
+ private val vibratorHelper: VibratorHelper?,
+ private val onTouchListener: KeyguardQuickAffordanceOnTouchListener
+ ) : View.OnLongClickListener {
+ override fun onLongClick(view: View): Boolean {
+ if (falsingManager?.isFalseLongTap(FalsingManager.MODERATE_PENALTY) == true) {
+ return true
+ }
+
+ if (viewModel.configKey != null) {
+ viewModel.onClicked(
+ KeyguardQuickAffordanceViewModel.OnClickedParameters(
+ configKey = viewModel.configKey,
+ expandable = Expandable.fromView(view),
+ slotId = viewModel.slotId,
+ )
+ )
+ vibratorHelper?.vibrate(
+ if (viewModel.isActivated) {
+ KeyguardBottomAreaVibrations.Activated
+ } else {
+ KeyguardBottomAreaVibrations.Deactivated
+ }
+ )
+ }
+
+ onTouchListener.cancel()
+ return true
+ }
+
+ override fun onLongClickUseDefaultHapticFeedback(view: View?) = false
+
+ }
+
private class OnClickListener(
private val viewModel: KeyguardQuickAffordanceViewModel,
private val falsingManager: FalsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
index 5745d6ae077e..7685345805f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceOnTouchListener.kt
@@ -46,7 +46,7 @@ class KeyguardQuickAffordanceOnTouchListener(
@SuppressLint("ClickableViewAccessibility")
override fun onTouch(v: View, event: MotionEvent): Boolean {
return when (event.actionMasked) {
- MotionEvent.ACTION_DOWN ->
+ MotionEvent.ACTION_DOWN -> {
if (viewModel.configKey != null) {
downDisplayCoords.set(event.rawX, event.rawY)
if (isUsingAccurateTool(event)) {
@@ -62,21 +62,10 @@ class KeyguardQuickAffordanceOnTouchListener(
.scaleX(PRESSED_SCALE)
.scaleY(PRESSED_SCALE)
.setDuration(longPressDurationMs)
- .withEndAction {
- if (
- falsingManager?.isFalseLongTap(
- FalsingManager.MODERATE_PENALTY
- ) == false
- ) {
- dispatchClick(viewModel.configKey)
- }
- cancel()
- }
}
- true
- } else {
- false
}
+ false
+ }
MotionEvent.ACTION_MOVE -> {
if (!isUsingAccurateTool(event)) {
// Moving too far while performing a long-press gesture cancels that
@@ -91,7 +80,7 @@ class KeyguardQuickAffordanceOnTouchListener(
cancel()
}
}
- true
+ false
}
MotionEvent.ACTION_UP -> {
if (isUsingAccurateTool(event)) {
@@ -146,7 +135,7 @@ class KeyguardQuickAffordanceOnTouchListener(
}
)
}
- true
+ false
}
MotionEvent.ACTION_CANCEL -> {
cancel()
@@ -179,7 +168,7 @@ class KeyguardQuickAffordanceOnTouchListener(
view.setOnClickListener(null)
}
- private fun cancel(onAnimationEnd: Runnable? = null) {
+ fun cancel(onAnimationEnd: Runnable? = null) {
longPressAnimator?.cancel()
longPressAnimator = null
view.animate().scaleX(1f).scaleY(1f).withEndAction(onAnimationEnd)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 2f72cb95db98..419d045885a8 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -654,6 +654,24 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void serviceProvidersUpdated_broadcastTriggersInfoRefresh() {
+ // The callback is invoked once on init
+ verify(mTestCallback, times(1)).onRefreshCarrierInfo();
+
+ // WHEN the SERVICE_PROVIDERS_UPDATED broadcast is sent
+ Intent intent = new Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED);
+ intent.putExtra(TelephonyManager.EXTRA_SPN, "spn");
+ intent.putExtra(TelephonyManager.EXTRA_PLMN, "plmn");
+ mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
+ putPhoneInfo(intent, null, true));
+ mTestableLooper.processAllMessages();
+
+ // THEN verify keyguardUpdateMonitorCallback receives a refresh callback
+ // Note that we have times(2) here because it's been called once already
+ verify(mTestCallback, times(2)).onRefreshCarrierInfo();
+ }
+
+ @Test
public void testTriesToAuthenticateFingerprint_whenKeyguard() {
mKeyguardUpdateMonitor.dispatchStartedGoingToSleep(0 /* why */);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index 9f5c181c3129..374c28d6dce8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -82,7 +82,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
// Wrong input.
- underTest.authenticate(listOf(9, 8, 7))
+ assertThat(underTest.authenticate(listOf(9, 8, 7))).isFalse()
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -90,7 +90,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PIN)
// Correct input.
- underTest.authenticate(listOf(1, 2, 3, 4))
+ assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -114,7 +114,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
// Wrong input.
- underTest.authenticate("alohamora".toList())
+ assertThat(underTest.authenticate("alohamora".toList())).isFalse()
assertThat(message).isEqualTo(MESSAGE_WRONG_PASSWORD)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -122,7 +122,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PASSWORD)
// Correct input.
- underTest.authenticate("password".toList())
+ assertThat(underTest.authenticate("password".toList())).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -146,9 +146,12 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
// Wrong input.
- underTest.authenticate(
- listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(3, 4))
- )
+ assertThat(
+ underTest.authenticate(
+ listOf(AuthenticationMethodModel.Pattern.PatternCoordinate(3, 4))
+ )
+ )
+ .isFalse()
assertThat(message).isEqualTo(MESSAGE_WRONG_PATTERN)
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -156,7 +159,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(message).isEqualTo(MESSAGE_ENTER_YOUR_PATTERN)
// Correct input.
- underTest.authenticate(emptyList())
+ assertThat(underTest.authenticate(emptyList())).isTrue()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
}
@@ -214,7 +217,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(isUnlocked).isFalse()
repeat(BouncerInteractor.THROTTLE_EVERY) { times ->
// Wrong PIN.
- underTest.authenticate(listOf(6, 7, 8, 9))
+ assertThat(underTest.authenticate(listOf(6, 7, 8, 9))).isFalse()
if (times < BouncerInteractor.THROTTLE_EVERY - 1) {
assertThat(message).isEqualTo(MESSAGE_WRONG_PIN)
}
@@ -223,7 +226,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC)
// Correct PIN, but throttled, so doesn't unlock:
- underTest.authenticate(listOf(1, 2, 3, 4))
+ assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isFalse()
assertThat(isUnlocked).isFalse()
assertTryAgainMessage(message, BouncerInteractor.THROTTLE_DURATION_SEC)
@@ -241,7 +244,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(isUnlocked).isFalse()
// Correct PIN and no longer throttled so unlocks:
- underTest.authenticate(listOf(1, 2, 3, 4))
+ assertThat(underTest.authenticate(listOf(1, 2, 3, 4))).isTrue()
assertThat(isUnlocked).isTrue()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
new file mode 100644
index 000000000000..1642410e5a3f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/AuthMethodBouncerViewModelTest.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bouncer.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.scene.SceneTestUtils
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class AuthMethodBouncerViewModelTest : SysuiTestCase() {
+
+ private val testScope = TestScope()
+ private val utils = SceneTestUtils(this, testScope)
+ private val authenticationInteractor =
+ utils.authenticationInteractor(
+ utils.authenticationRepository(),
+ )
+ private val underTest =
+ PinBouncerViewModel(
+ applicationScope = testScope.backgroundScope,
+ interactor =
+ utils.bouncerInteractor(
+ authenticationInteractor = authenticationInteractor,
+ sceneInteractor = utils.sceneInteractor(),
+ ),
+ isInputEnabled = MutableStateFlow(true),
+ )
+
+ @Test
+ fun animateFailure() =
+ testScope.runTest {
+ authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
+ val animateFailure by collectLastValue(underTest.animateFailure)
+ assertThat(animateFailure).isFalse()
+
+ // Wrong PIN:
+ underTest.onPinButtonClicked(3)
+ underTest.onPinButtonClicked(4)
+ underTest.onPinButtonClicked(5)
+ underTest.onPinButtonClicked(6)
+ underTest.onAuthenticateButtonClicked()
+ assertThat(animateFailure).isTrue()
+
+ underTest.onFailureAnimationShown()
+ assertThat(animateFailure).isFalse()
+
+ // Correct PIN:
+ underTest.onPinButtonClicked(1)
+ underTest.onPinButtonClicked(2)
+ underTest.onPinButtonClicked(3)
+ underTest.onPinButtonClicked(4)
+ underTest.onAuthenticateButtonClicked()
+ assertThat(animateFailure).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index b942ccbb51f3..e8c946cdd59d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -93,22 +93,21 @@ class BouncerViewModelTest : SysuiTestCase() {
}
@Test
- fun isMessageUpdateAnimationsEnabled() =
+ fun message() =
testScope.runTest {
- val isMessageUpdateAnimationsEnabled by
- collectLastValue(underTest.isMessageUpdateAnimationsEnabled)
+ val message by collectLastValue(underTest.message)
val throttling by collectLastValue(bouncerInteractor.throttling)
authenticationInteractor.setAuthenticationMethod(AuthenticationMethodModel.PIN(1234))
- assertThat(isMessageUpdateAnimationsEnabled).isTrue()
+ assertThat(message?.isUpdateAnimated).isTrue()
repeat(BouncerInteractor.THROTTLE_EVERY) {
// Wrong PIN.
bouncerInteractor.authenticate(listOf(3, 4, 5, 6))
}
- assertThat(isMessageUpdateAnimationsEnabled).isFalse()
+ assertThat(message?.isUpdateAnimated).isFalse()
throttling?.totalDurationSec?.let { seconds -> advanceTimeBy(seconds * 1000L) }
- assertThat(isMessageUpdateAnimationsEnabled).isTrue()
+ assertThat(message?.isUpdateAnimated).isTrue()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
index b7b90de3b54a..f436aa309ac5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PasswordBouncerViewModelTest.kt
@@ -85,7 +85,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onShown()
- assertThat(message).isEqualTo(ENTER_YOUR_PASSWORD)
+ assertThat(message?.text).isEqualTo(ENTER_YOUR_PASSWORD)
assertThat(password).isEqualTo("")
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -109,7 +109,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onPasswordInputChanged("password")
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
assertThat(password).isEqualTo("password")
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -156,7 +156,7 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
- assertThat(message).isEqualTo(WRONG_PASSWORD)
+ assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -179,13 +179,13 @@ class PasswordBouncerViewModelTest : SysuiTestCase() {
underTest.onPasswordInputChanged("wrong")
underTest.onAuthenticateKeyPressed()
assertThat(password).isEqualTo("")
- assertThat(message).isEqualTo(WRONG_PASSWORD)
+ assertThat(message?.text).isEqualTo(WRONG_PASSWORD)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
// Enter the correct password:
underTest.onPasswordInputChanged("password")
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
underTest.onAuthenticateKeyPressed()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
index b588ba2b2574..d7d7154705ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PatternBouncerViewModelTest.kt
@@ -89,7 +89,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onShown()
- assertThat(message).isEqualTo(ENTER_YOUR_PATTERN)
+ assertThat(message?.text).isEqualTo(ENTER_YOUR_PATTERN)
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(isUnlocked).isFalse()
@@ -115,7 +115,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onDragStart()
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
assertThat(isUnlocked).isFalse()
@@ -202,7 +202,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
- assertThat(message).isEqualTo(WRONG_PATTERN)
+ assertThat(message?.text).isEqualTo(WRONG_PATTERN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -235,7 +235,7 @@ class PatternBouncerViewModelTest : SysuiTestCase() {
underTest.onDragEnd()
assertThat(selectedDots).isEmpty()
assertThat(currentDot).isNull()
- assertThat(message).isEqualTo(WRONG_PATTERN)
+ assertThat(message?.text).isEqualTo(WRONG_PATTERN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 83f9687d7ac5..3bdaf0590888 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -94,7 +94,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onShown()
- assertThat(message).isEqualTo(ENTER_YOUR_PIN)
+ assertThat(message?.text).isEqualTo(ENTER_YOUR_PIN)
assertThat(pinLengths).isEqualTo(0 to 0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -116,7 +116,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(1)
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
assertThat(pinLengths).isEqualTo(0 to 1)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -140,7 +140,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onBackspaceButtonClicked()
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
assertThat(pinLengths).isEqualTo(1 to 0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -170,7 +170,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
advanceTimeBy(PinBouncerViewModel.BACKSPACE_LONG_PRESS_DELAY_MS)
}
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
assertThat(pinLengths).isEqualTo(1 to 0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -220,7 +220,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onAuthenticateButtonClicked()
assertThat(pinLengths).isEqualTo(0 to 0)
- assertThat(message).isEqualTo(WRONG_PIN)
+ assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
}
@@ -244,7 +244,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(4)
underTest.onPinButtonClicked(5) // PIN is now wrong!
underTest.onAuthenticateButtonClicked()
- assertThat(message).isEqualTo(WRONG_PIN)
+ assertThat(message?.text).isEqualTo(WRONG_PIN)
assertThat(pinLengths).isEqualTo(0 to 0)
assertThat(isUnlocked).isFalse()
assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
@@ -254,7 +254,7 @@ class PinBouncerViewModelTest : SysuiTestCase() {
underTest.onPinButtonClicked(2)
underTest.onPinButtonClicked(3)
underTest.onPinButtonClicked(4)
- assertThat(message).isEmpty()
+ assertThat(message?.text).isEmpty()
underTest.onAuthenticateButtonClicked()
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index f31ca8178eb6..c2ebddf00fb4 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -2057,7 +2057,11 @@ public class CameraExtensionsProxyService extends Service {
mIsImageValid = false;
if (mGraphicBuffer != null) {
- ImageReader.unlockGraphicBuffer(mGraphicBuffer);
+ try {
+ ImageReader.unlockGraphicBuffer(mGraphicBuffer);
+ } catch (RuntimeException e) {
+ e.printStackTrace();
+ }
mGraphicBuffer.destroy();
mGraphicBuffer = null;
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 12bb5d23e4cf..76a994ec63e9 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -151,6 +151,8 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@@ -1707,7 +1709,8 @@ class UserController implements Handler.Callback {
mInjector.getWindowManager().setSwitchingUser(true);
// Only lock if the user has a secure keyguard PIN/Pattern/Pwd
if (mInjector.getKeyguardManager().isDeviceSecure(userId)) {
- mInjector.getWindowManager().lockNow(null);
+ // Make sure the device is locked before moving on with the user switch
+ mInjector.lockDeviceNowAndWaitForKeyguardShown();
}
}
@@ -3444,6 +3447,11 @@ class UserController implements Handler.Callback {
WindowManagerService getWindowManager() {
return mService.mWindowManager;
}
+
+ ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+ return mService.mAtmInternal;
+ }
+
void activityManagerOnUserStopped(@UserIdInt int userId) {
LocalServices.getService(ActivityTaskManagerInternal.class).onUserStopped(userId);
}
@@ -3667,5 +3675,43 @@ class UserController implements Handler.Callback {
void onSystemUserVisibilityChanged(boolean visible) {
getUserManagerInternal().onSystemUserVisibilityChanged(visible);
}
+
+ void lockDeviceNowAndWaitForKeyguardShown() {
+ if (getWindowManager().isKeyguardLocked()) {
+ return;
+ }
+
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("lockDeviceNowAndWaitForKeyguardShown");
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ ActivityTaskManagerInternal.ScreenObserver screenObserver =
+ new ActivityTaskManagerInternal.ScreenObserver() {
+ @Override
+ public void onAwakeStateChanged(boolean isAwake) {
+
+ }
+
+ @Override
+ public void onKeyguardStateChanged(boolean isShowing) {
+ if (isShowing) {
+ latch.countDown();
+ }
+ }
+ };
+
+ getActivityTaskManagerInternal().registerScreenObserver(screenObserver);
+ getWindowManager().lockDeviceNow();
+ try {
+ if (!latch.await(20, TimeUnit.SECONDS)) {
+ throw new RuntimeException("Keyguard is not shown in 20 seconds");
+ }
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } finally {
+ getActivityTaskManagerInternal().unregisterScreenObserver(screenObserver);
+ t.traceEnd();
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 662591e3d264..4cb22dbcf308 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -500,8 +500,6 @@ public class InputManagerService extends IInputManager.Stub
// Add ourselves to the Watchdog monitors.
Watchdog.getInstance().addMonitor(this);
-
- mSettingsObserver.registerAndUpdate();
}
// TODO(BT) Pass in parameter for bluetooth system
@@ -512,6 +510,8 @@ public class InputManagerService extends IInputManager.Stub
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
+ mSettingsObserver.registerAndUpdate();
+
synchronized (mLidSwitchLock) {
mSystemReady = true;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a148de6848d0..9c780bb90b22 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1086,6 +1086,8 @@ public class PhoneWindowManager implements WindowManagerPolicy {
final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal();
if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) {
+ Slog.d(TAG, "Can't start dreaming when attempting to dream from short power"
+ + " press (isScreenOn=" + isScreenOn + ")");
noDreamAction.run();
return;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index e07c65426278..32f1f42aacb9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -262,9 +262,17 @@ public abstract class ActivityTaskManagerInternal {
*/
public abstract void setVr2dDisplayId(int vr2dDisplayId);
+ /**
+ * Registers a {@link ScreenObserver}.
+ */
public abstract void registerScreenObserver(ScreenObserver observer);
/**
+ * Unregisters the given {@link ScreenObserver}.
+ */
+ public abstract void unregisterScreenObserver(ScreenObserver observer);
+
+ /**
* Returns is the caller has the same uid as the Recents component
*/
public abstract boolean isCallerRecents(int callingUid);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 89862918744c..4d4ffbafdcad 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -296,6 +296,7 @@ import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
@@ -652,7 +653,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
*/
float mMinPercentageMultiWindowSupportWidth;
- final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers = new ArrayList<>();
+ final List<ActivityTaskManagerInternal.ScreenObserver> mScreenObservers =
+ Collections.synchronizedList(new ArrayList<>());
// VR Vr2d Display Id.
int mVr2dDisplayId = INVALID_DISPLAY;
@@ -5869,6 +5871,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
}
@Override
+ public void unregisterScreenObserver(ScreenObserver observer) {
+ mScreenObservers.remove(observer);
+ }
+
+ @Override
public boolean isCallerRecents(int callingUid) {
return ActivityTaskManagerService.this.isCallerRecents(callingUid);
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e44d27911262..6aec96988a77 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -6511,6 +6511,22 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
.getKeyguardController().isDisplayOccluded(mDisplayId);
}
+ /**
+ * @return the task that is occluding the keyguard
+ */
+ @Nullable
+ Task getTaskOccludingKeyguard() {
+ final KeyguardController keyguardController = mRootWindowContainer.mTaskSupervisor
+ .getKeyguardController();
+ if (keyguardController.getTopOccludingActivity(mDisplayId) != null) {
+ return keyguardController.getTopOccludingActivity(mDisplayId).getRootTask();
+ }
+ if (keyguardController.getDismissKeyguardActivity(mDisplayId) != null) {
+ return keyguardController.getDismissKeyguardActivity(mDisplayId).getRootTask();
+ }
+ return null;
+ }
+
@VisibleForTesting
void removeAllTasks() {
forAllTasks((t) -> { t.getRootTask().removeChild(t, "removeAllTasks"); });
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 5f6d66011768..99878a3d0ffe 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -17,7 +17,6 @@
package com.android.server.wm;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
@@ -411,19 +410,20 @@ class KeyguardController {
if (waitAppTransition) {
mService.deferWindowLayout();
try {
- mRootWindowContainer.getDefaultDisplay()
- .requestTransitionAndLegacyPrepare(
- isDisplayOccluded(DEFAULT_DISPLAY)
- ? TRANSIT_KEYGUARD_OCCLUDE
- : TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+ if (isDisplayOccluded(DEFAULT_DISPLAY)) {
+ mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
+ TRANSIT_KEYGUARD_OCCLUDE,
+ topActivity != null ? topActivity.getRootTask() : null);
+ } else {
+ mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
+ TRANSIT_KEYGUARD_UNOCCLUDE, 0 /* flags */);
+ }
updateKeyguardSleepToken(DEFAULT_DISPLAY);
mWindowManager.executeAppTransition();
} finally {
mService.continueWindowLayout();
}
}
- dismissMultiWindowModeForTaskIfNeeded(displayId, topActivity != null
- ? topActivity.getRootTask() : null);
}
/**
@@ -473,6 +473,14 @@ class KeyguardController {
return getDisplayState(displayId).mOccluded;
}
+ ActivityRecord getTopOccludingActivity(int displayId) {
+ return getDisplayState(displayId).mTopOccludesActivity;
+ }
+
+ ActivityRecord getDismissKeyguardActivity(int displayId) {
+ return getDisplayState(displayId).mDismissingKeyguardActivity;
+ }
+
/**
* @return true if Keyguard can be currently dismissed without entering credentials.
*/
@@ -488,22 +496,6 @@ class KeyguardController {
return getDisplayState(DEFAULT_DISPLAY).mShowingDream;
}
- private void dismissMultiWindowModeForTaskIfNeeded(int displayId,
- @Nullable Task currentTaskControllingOcclusion) {
- // TODO(b/113840485): Handle docked stack for individual display.
- if (!getDisplayState(displayId).mKeyguardShowing || !isDisplayOccluded(DEFAULT_DISPLAY)) {
- return;
- }
-
- // Dismiss freeform windowing mode
- if (currentTaskControllingOcclusion == null) {
- return;
- }
- if (currentTaskControllingOcclusion.inFreeformWindowingMode()) {
- currentTaskControllingOcclusion.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
- }
- }
-
private void updateKeyguardSleepToken() {
for (int displayNdx = mRootWindowContainer.getChildCount() - 1;
displayNdx >= 0; displayNdx--) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 536915929e88..7e839596c59b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2376,6 +2376,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
if (!displayShouldSleep && display.mTransitionController.isShellTransitionsEnabled()
&& !display.mTransitionController.isCollecting()) {
int transit = TRANSIT_NONE;
+ Task startTask = null;
if (!display.getDisplayPolicy().isAwake()) {
// Note that currently this only happens on default display because non-default
// display is always awake.
@@ -2383,12 +2384,12 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
} else if (display.isKeyguardOccluded()) {
// The display was awake so this is resuming activity for occluding keyguard.
transit = WindowManager.TRANSIT_KEYGUARD_OCCLUDE;
+ startTask = display.getTaskOccludingKeyguard();
}
if (transit != TRANSIT_NONE) {
display.mTransitionController.requestStartTransition(
display.mTransitionController.createTransition(transit),
- null /* startTask */, null /* remoteTransition */,
- null /* displayChange */);
+ startTask, null /* remoteTransition */, null /* displayChange */);
}
}
// Set the sleeping state of the root tasks on the display.
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index d5a7ff582a58..b938b9e4ec51 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2249,8 +2249,10 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener {
while (leashReference.getParent() != ancestor) {
leashReference = leashReference.getParent();
}
+
final SurfaceControl rootLeash = leashReference.makeAnimationLeash().setName(
"Transition Root: " + leashReference.getName()).build();
+ rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots");
startT.setLayer(rootLeash, leashReference.getLastLayer());
outInfo.addRootLeash(endDisplayId, rootLeash,
ancestor.getBounds().left, ancestor.getBounds().top);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 317fd58e0c76..dccacb4d301a 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -58,6 +58,7 @@ import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -103,11 +104,13 @@ import com.android.server.am.UserState.KeyEvictedCallback;
import com.android.server.pm.UserJourneyLogger;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
+import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.WindowManagerService;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import java.util.ArrayList;
import java.util.Arrays;
@@ -187,6 +190,7 @@ public class UserControllerTest {
doNothing().when(mInjector).activityManagerOnUserStopped(anyInt());
doNothing().when(mInjector).clearBroadcastQueueForUser(anyInt());
doNothing().when(mInjector).taskSupervisorRemoveUser(anyInt());
+ doNothing().when(mInjector).lockDeviceNowAndWaitForKeyguardShown();
mockIsUsersOnSecondaryDisplaysEnabled(false);
// All UserController params are set to default.
@@ -951,6 +955,45 @@ public class UserControllerTest {
.systemServiceManagerOnUserCompletedEvent(eq(user2), eq(event2a));
}
+ @Test
+ public void testStallUserSwitchUntilTheKeyguardIsShown() throws Exception {
+ // enable user switch ui, because keyguard is only shown then
+ mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
+ /* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
+
+ // mock the device to be secure in order to expect the keyguard to be shown
+ when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
+
+ // call real lockDeviceNowAndWaitForKeyguardShown method for this test
+ doCallRealMethod().when(mInjector).lockDeviceNowAndWaitForKeyguardShown();
+
+ // call startUser on a thread because we're expecting it to be blocked
+ Thread threadStartUser = new Thread(()-> {
+ mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
+ });
+ threadStartUser.start();
+
+ // make sure the switch is stalled...
+ Thread.sleep(2000);
+ // by checking REPORT_USER_SWITCH_MSG is not sent yet
+ assertNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG));
+ // and the thread is still alive
+ assertTrue(threadStartUser.isAlive());
+
+ // mock send the keyguard shown event
+ ArgumentCaptor<ActivityTaskManagerInternal.ScreenObserver> captor = ArgumentCaptor.forClass(
+ ActivityTaskManagerInternal.ScreenObserver.class);
+ verify(mInjector.mActivityTaskManagerInternal).registerScreenObserver(captor.capture());
+ captor.getValue().onKeyguardStateChanged(true);
+
+ // verify the switch now moves on...
+ Thread.sleep(1000);
+ // by checking REPORT_USER_SWITCH_MSG is sent
+ assertNotNull(mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG));
+ // and the thread is finished
+ assertFalse(threadStartUser.isAlive());
+ }
+
private void setUpAndStartUserInBackground(int userId) throws Exception {
setUpUser(userId, 0);
mUserController.startUser(userId, USER_START_MODE_BACKGROUND);
@@ -1092,6 +1135,7 @@ public class UserControllerTest {
private final IStorageManager mStorageManagerMock;
private final UserManagerInternal mUserManagerInternalMock;
private final WindowManagerService mWindowManagerMock;
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
private final KeyguardManager mKeyguardManagerMock;
private final LockPatternUtils mLockPatternUtilsMock;
@@ -1111,6 +1155,7 @@ public class UserControllerTest {
mUserManagerMock = mock(UserManagerService.class);
mUserManagerInternalMock = mock(UserManagerInternal.class);
mWindowManagerMock = mock(WindowManagerService.class);
+ mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
mStorageManagerMock = mock(IStorageManager.class);
mKeyguardManagerMock = mock(KeyguardManager.class);
when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
@@ -1172,6 +1217,11 @@ public class UserControllerTest {
}
@Override
+ ActivityTaskManagerInternal getActivityTaskManagerInternal() {
+ return mActivityTaskManagerInternal;
+ }
+
+ @Override
KeyguardManager getKeyguardManager() {
return mKeyguardManagerMock;
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
index 7f496d828996..122a6cbbb98f 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnGoHomeTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
@@ -102,6 +103,7 @@ open class CloseImeOnGoHomeTest(flicker: FlickerTest) : BaseTest(flicker) {
@Presubmit
@Test
+ @PlatinumTest(focusArea = "ime")
@IwTest(focusArea = "ime")
override fun cujCompleted() {
super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
index c693ca759bdd..5aa43820e5cb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToAppOnPressBackTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -103,6 +104,7 @@ open class CloseImeToAppOnPressBackTest(flicker: FlickerTest) : BaseTest(flicker
@Presubmit
@Test
+ @PlatinumTest(focusArea = "ime")
@IwTest(focusArea = "ime")
override fun cujCompleted() {
super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
index d5208e060883..6731089f8903 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeToHomeOnFinishActivityTest.kt
@@ -18,6 +18,7 @@ package com.android.server.wm.flicker.ime
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -71,6 +72,7 @@ open class CloseImeToHomeOnFinishActivityTest(flicker: FlickerTest) : BaseTest(f
@Presubmit
@Test
+ @PlatinumTest(focusArea = "ime")
@IwTest(focusArea = "ime")
override fun cujCompleted() {
runAndIgnoreAssumptionViolation { entireScreenCovered() }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
index d1335294f629..c70e3cb773d4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhenFocusingOnInputFieldTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.ime
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -52,6 +53,7 @@ open class ShowImeWhenFocusingOnInputFieldTest(flicker: FlickerTest) : BaseTest(
@Presubmit
@Test
+ @PlatinumTest(focusArea = "ime")
@IwTest(focusArea = "ime")
override fun cujCompleted() {
super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
index 855ea3e38b3e..7fbcfec40121 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/ChangeAppRotationTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -129,6 +130,7 @@ open class ChangeAppRotationTest(flicker: FlickerTest) : RotationTransition(flic
}
@Test
+ @PlatinumTest(focusArea = "framework")
@IwTest(focusArea = "framework")
override fun cujCompleted() {
super.cujCompleted()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
index 0cbbb83b48ee..44ae14a8e4bb 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.rotation
import android.platform.test.annotations.IwTest
+import android.platform.test.annotations.PlatinumTest
import android.platform.test.annotations.Presubmit
import android.tools.common.ScenarioBuilder
import android.tools.common.traces.component.ComponentNameMatcher
@@ -213,6 +214,7 @@ open class SeamlessAppRotationTest(flicker: FlickerTest) : RotationTransition(fl
}
@Test
+ @PlatinumTest(focusArea = "framework")
@IwTest(focusArea = "framework")
override fun cujCompleted() {
appWindowFullScreen()