Merge "Use valid auto-group notification trigger key for sparse groups" into main
diff --git a/core/api/current.txt b/core/api/current.txt
index 520c7d1..6d6f7e3 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -19760,7 +19760,7 @@
public final class CaptureRequest extends android.hardware.camera2.CameraMetadata<android.hardware.camera2.CaptureRequest.Key<?>> implements android.os.Parcelable {
method public int describeContents();
- method @FlaggedApi("com.android.internal.camera.flags.surface_leak_fix") protected void finalize();
+ method protected void finalize();
method @Nullable public <T> T get(android.hardware.camera2.CaptureRequest.Key<T>);
method @NonNull public java.util.List<android.hardware.camera2.CaptureRequest.Key<?>> getKeys();
method @Nullable public Object getTag();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 1e94c2f..24962a3 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -384,10 +384,6 @@
field public static final int DEVICE_INITIAL_SDK_INT;
}
- public class Environment {
- method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeDirectory();
- }
-
public class IpcDataCache<Query, Result> {
ctor public IpcDataCache(int, @NonNull String, @NonNull String, @NonNull String, @NonNull android.os.IpcDataCache.QueryHandler<Query,Result>);
method public void disableForCurrentProcess();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 9c807af..7a8e829 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10773,6 +10773,7 @@
public class Environment {
method @NonNull public static java.io.File getDataCePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
method @NonNull public static java.io.File getDataDePackageDirectoryForUser(@NonNull java.util.UUID, @NonNull android.os.UserHandle, @NonNull String);
+ method @FlaggedApi("android.crashrecovery.flags.enable_crashrecovery") @NonNull public static java.io.File getDataSystemDeDirectory();
method @NonNull public static java.util.Collection<java.io.File> getInternalMediaDirectories();
method @NonNull public static java.io.File getOdmDirectory();
method @NonNull public static java.io.File getOemDirectory();
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 081dfe6..d9f886d 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -134,6 +134,14 @@
bug: "364338410"
}
+flag {
+ name: "lock_now_coexistence"
+ is_exported: true
+ namespace: "enterprise"
+ description: "Enables coexistence support for lockNow."
+ bug: "366559840"
+}
+
# Fully rolled out and must not be used.
flag {
name: "security_log_v2_enabled"
@@ -323,3 +331,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "user_provisioning_same_state"
+ namespace: "enterprise"
+ description: "Handle exceptions while setting same provisioning state."
+ bug: "326441417"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/android/app/appfunctions/TEST_MAPPING b/core/java/android/app/appfunctions/TEST_MAPPING
new file mode 100644
index 0000000..91e82ec
--- /dev/null
+++ b/core/java/android/app/appfunctions/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "postsubmit": [
+ {
+ "name": "FrameworksAppFunctionsTests"
+ },
+ {
+ "name": "CtsAppFunctionTestCases"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index ac72116..3b69aa7 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -612,9 +612,7 @@
Parcelable[] parcelableArray = in.readParcelableArray(Surface.class.getClassLoader(),
Surface.class);
if (parcelableArray != null) {
- if (Flags.surfaceLeakFix()) {
- mReleaseSurfaces = true;
- }
+ mReleaseSurfaces = true;
for (Parcelable p : parcelableArray) {
Surface s = (Surface) p;
mSurfaceSet.add(s);
@@ -798,7 +796,6 @@
}
@SuppressWarnings("Finalize")
- @FlaggedApi(Flags.FLAG_SURFACE_LEAK_FIX)
@Override
protected void finalize() {
if (mReleaseSurfaces) {
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index 0ec5c0a..eead4ef 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -372,11 +372,9 @@
Map<String, CameraMetadataNative> charsMap, OutputSurface previewSurface,
OutputSurface imageCaptureSurface, OutputSurface postviewSurface)
throws RemoteException {
- if (Flags.surfaceLeakFix()) {
- mPreviewSurface = previewSurface;
- mPostviewSurface = postviewSurface;
- mImageCaptureSurface = imageCaptureSurface;
- }
+ mPreviewSurface = previewSurface;
+ mPostviewSurface = postviewSurface;
+ mImageCaptureSurface = imageCaptureSurface;
ExtensionConfiguration config = SessionProcessor.this.initSession(token, cameraId,
new CharacteristicsMap(charsMap),
new CameraOutputSurface(previewSurface),
@@ -399,16 +397,14 @@
@Override
public void deInitSession(IBinder token) throws RemoteException {
SessionProcessor.this.deInitSession(token);
- if (Flags.surfaceLeakFix()) {
- if ((mPreviewSurface != null) && (mPreviewSurface.surface != null)) {
- mPreviewSurface.surface.release();
- }
- if ((mImageCaptureSurface != null) && (mImageCaptureSurface.surface != null)) {
- mImageCaptureSurface.surface.release();
- }
- if ((mPostviewSurface != null) && (mPostviewSurface.surface != null)) {
- mPostviewSurface.surface.release();
- }
+ if ((mPreviewSurface != null) && (mPreviewSurface.surface != null)) {
+ mPreviewSurface.surface.release();
+ }
+ if ((mImageCaptureSurface != null) && (mImageCaptureSurface.surface != null)) {
+ mImageCaptureSurface.surface.release();
+ }
+ if ((mPostviewSurface != null) && (mPostviewSurface.surface != null)) {
+ mPostviewSurface.surface.release();
}
}
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index c7ebc63..0cabc4c 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -65,31 +65,34 @@
public static final int KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS = 23;
public static final int KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK = 24;
public static final int KEY_GESTURE_TYPE_SYSTEM_MUTE = 25;
- public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION = 26;
- public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS = 27;
- public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 28;
- public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 29;
- public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 30;
- public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 31;
- public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 32;
- public static final int KEY_GESTURE_TYPE_SLEEP = 33;
- public static final int KEY_GESTURE_TYPE_WAKEUP = 34;
- public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 35;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 36;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 37;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 38;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 39;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 40;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 41;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 42;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 43;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 44;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 45;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 46;
- public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 47;
- public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 48;
- public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 49;
- public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 50;
+ public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT = 26;
+ public static final int KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT = 27;
+ public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT = 28;
+ public static final int KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT = 29;
+ public static final int KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT = 30;
+ public static final int KEY_GESTURE_TYPE_LOCK_SCREEN = 31;
+ public static final int KEY_GESTURE_TYPE_OPEN_NOTES = 32;
+ public static final int KEY_GESTURE_TYPE_TOGGLE_POWER = 33;
+ public static final int KEY_GESTURE_TYPE_SYSTEM_NAVIGATION = 34;
+ public static final int KEY_GESTURE_TYPE_SLEEP = 35;
+ public static final int KEY_GESTURE_TYPE_WAKEUP = 36;
+ public static final int KEY_GESTURE_TYPE_MEDIA_KEY = 37;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_BROWSER = 38;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_EMAIL = 39;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CONTACTS = 40;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALENDAR = 41;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_CALCULATOR = 42;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MUSIC = 43;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MAPS = 44;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_MESSAGING = 45;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_GALLERY = 46;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FILES = 47;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_WEATHER = 48;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_DEFAULT_FITNESS = 49;
+ public static final int KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME = 50;
+ public static final int KEY_GESTURE_TYPE_DESKTOP_MODE = 51;
+ public static final int KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION = 52;
+ public static final int KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER = 53;
public static final int FLAG_CANCELLED = 1;
@@ -130,8 +133,10 @@
KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
KEY_GESTURE_TYPE_SYSTEM_MUTE,
- KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
- KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+ KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
+ KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
KEY_GESTURE_TYPE_LOCK_SCREEN,
KEY_GESTURE_TYPE_OPEN_NOTES,
@@ -155,6 +160,7 @@
KEY_GESTURE_TYPE_LAUNCH_APPLICATION_BY_PACKAGE_NAME,
KEY_GESTURE_TYPE_DESKTOP_MODE,
KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+ KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
})
@Retention(RetentionPolicy.SOURCE)
public @interface KeyGestureType {
@@ -331,6 +337,7 @@
case KEY_GESTURE_TYPE_HOME:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__HOME;
case KEY_GESTURE_TYPE_RECENT_APPS:
+ case KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__RECENT_APPS;
case KEY_GESTURE_TYPE_BACK:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__BACK;
@@ -378,9 +385,11 @@
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TOGGLE_CAPS_LOCK;
case KEY_GESTURE_TYPE_SYSTEM_MUTE:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SYSTEM_MUTE;
- case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
+ case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+ case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__SPLIT_SCREEN_NAVIGATION;
- case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
+ case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+ case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__CHANGE_SPLITSCREEN_FOCUS;
case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
return FrameworkStatsLog.KEYBOARD_SYSTEMS_EVENT_REPORTED__KEYBOARD_SYSTEM_EVENT__TRIGGER_BUG_REPORT;
@@ -487,10 +496,14 @@
return "KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK";
case KEY_GESTURE_TYPE_SYSTEM_MUTE:
return "KEY_GESTURE_TYPE_SYSTEM_MUTE";
- case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION:
- return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION";
- case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS:
- return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS";
+ case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+ return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT";
+ case KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
+ return "KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT";
+ case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+ return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT";
+ case KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
+ return "KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT";
case KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
return "KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT";
case KEY_GESTURE_TYPE_LOCK_SCREEN:
@@ -537,6 +550,8 @@
return "KEY_GESTURE_TYPE_DESKTOP_MODE";
case KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
return "KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION";
+ case KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
+ return "KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER";
default:
return Integer.toHexString(value);
}
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 4478592..1a309c6 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -112,6 +112,13 @@
}
flag {
+ namespace: "input_native"
+ name: "use_key_gesture_event_handler"
+ description: "Use KeyGestureEvent handler APIs to control system shortcuts and key gestures"
+ bug: "358569822"
+}
+
+flag {
name: "keyboard_repeat_keys"
namespace: "input_native"
description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 2c7b9c0..89a5e5d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -415,7 +415,7 @@
* Returns the base directory for per-user system directory, device encrypted.
* {@hide}
*/
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @SystemApi
@FlaggedApi(android.crashrecovery.flags.Flags.FLAG_ENABLE_CRASHRECOVERY)
public static @NonNull File getDataSystemDeDirectory() {
return buildPath(getDataDirectory(), "system_de");
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index c7900e4..668cd01 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -22,6 +22,7 @@
import android.os.Build;
import android.os.ChildZygoteProcess;
import android.os.Process;
+import android.os.UserHandle;
import android.os.ZygoteProcess;
import android.text.TextUtils;
import android.util.Log;
@@ -141,12 +142,14 @@
String abi = sPackage.applicationInfo.primaryCpuAbi;
int runtimeFlags = Zygote.getMemorySafetyRuntimeFlagsForSecondaryZygote(
sPackage.applicationInfo, null);
+ final int[] sharedAppGid = {
+ UserHandle.getSharedAppGid(UserHandle.getAppId(sPackage.applicationInfo.uid)) };
sZygote = Process.ZYGOTE_PROCESS.startChildZygote(
"com.android.internal.os.WebViewZygoteInit",
"webview_zygote",
Process.WEBVIEW_ZYGOTE_UID,
Process.WEBVIEW_ZYGOTE_UID,
- null, // gids
+ sharedAppGid, // Access to shared app GID for ART profiles
runtimeFlags,
"webview_zygote", // seInfo
abi, // abi
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 69b91fd..e1402f8 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -246,15 +246,6 @@
}
flag {
- name: "custom_animations_behind_translucent"
- namespace: "windowing_frontend"
- description: "A change can use its own layer parameters to animate behind a translucent activity"
- bug: "327332488"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-flag {
name: "migrate_predictive_back_transition"
namespace: "windowing_frontend"
description: "Create transition when visibility change from predictive back"
@@ -274,4 +265,4 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 46fe68f..0caa8e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -55,6 +55,7 @@
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
+import com.android.wm.shell.shared.desktopmode.DesktopModeFlags;
import com.android.wm.shell.windowdecor.extension.TaskInfoKt;
import com.android.wm.shell.windowdecor.viewhost.WindowDecorViewHostSupplier;
@@ -240,7 +241,8 @@
boolean applyStartTransactionOnDraw, boolean setTaskCropAndPosition) {
final boolean isFreeform =
taskInfo.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_FREEFORM;
- final boolean isDragResizeable = isFreeform && taskInfo.isResizeable;
+ final boolean isDragResizeable = DesktopModeFlags.SCALED_RESIZING.isEnabled(mContext)
+ ? isFreeform : isFreeform && taskInfo.isResizeable;
final WindowDecorLinearLayout oldRootView = mResult.mRootView;
final SurfaceControl oldDecorationSurface = mDecorationContainerSurface;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index 2519ce4..b311359 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -1603,14 +1603,27 @@
Transitions transitions,
InteractionJankMonitor interactionJankMonitor,
Supplier<SurfaceControl.Transaction> transactionFactory) {
- if (!DesktopModeStatus.isVeiledResizeEnabled()) {
- return new FluidResizeTaskPositioner(
- taskOrganizer, transitions, windowDecoration, displayController,
- dragStartListener, transactionFactory);
+ final TaskPositioner taskPositioner = DesktopModeStatus.isVeiledResizeEnabled()
+ ? new VeiledResizeTaskPositioner(
+ taskOrganizer,
+ windowDecoration,
+ displayController,
+ dragStartListener,
+ transitions,
+ interactionJankMonitor)
+ : new FluidResizeTaskPositioner(
+ taskOrganizer,
+ transitions,
+ windowDecoration,
+ displayController,
+ dragStartListener,
+ transactionFactory);
+
+ if (DesktopModeFlags.SCALED_RESIZING.isEnabled(windowDecoration.mContext)) {
+ return new FixedAspectRatioTaskPositionerDecorator(windowDecoration,
+ taskPositioner);
}
- return new VeiledResizeTaskPositioner(
- taskOrganizer, windowDecoration, displayController,
- dragStartListener, transitions, interactionJankMonitor);
+ return taskPositioner;
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index 5d16d97..5521c2e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -455,7 +455,7 @@
}
private void updateDragResizeListener(SurfaceControl oldDecorationSurface) {
- if (!isDragResizable(mTaskInfo)) {
+ if (!isDragResizable(mTaskInfo, mContext)) {
if (!mTaskInfo.positionInParent.equals(mPositionInParent)) {
// We still want to track caption bar's exclusion region on a non-resizeable task.
updateExclusionRegion();
@@ -497,12 +497,16 @@
}
}
- private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo) {
+ private static boolean isDragResizable(ActivityManager.RunningTaskInfo taskInfo,
+ Context context) {
+ if (DesktopModeFlags.SCALED_RESIZING.isEnabled(context)) {
+ return taskInfo.isFreeform();
+ }
return taskInfo.isFreeform() && taskInfo.isResizeable;
}
private void updateMaximizeMenu(SurfaceControl.Transaction startT) {
- if (!isDragResizable(mTaskInfo) || !isMaximizeMenuActive()) {
+ if (!isDragResizable(mTaskInfo, mContext) || !isMaximizeMenuActive()) {
return;
}
if (!mTaskInfo.isVisible()) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 1741fe4..69efdb8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -107,6 +107,7 @@
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -563,6 +564,7 @@
}
@Test
+ @Ignore("TODO(b/367235906): Due to MONITOR_INPUT permission error")
public void relayout_freeformTask_appliesTransactionOnDraw() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo));
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index f33a744..7b9ff23 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.ParcelFileDescriptor;
+import android.os.Trace;
import android.util.AndroidRuntimeException;
import android.util.Log;
@@ -314,8 +315,13 @@
public final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate) {
// FIXME: b/174876164 implement device id for soundpool
- baseStart(0);
- return _play(soundID, leftVolume, rightVolume, priority, loop, rate, getPlayerIId());
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_AUDIO, "SoundPool.play");
+ baseStart(0);
+ return _play(soundID, leftVolume, rightVolume, priority, loop, rate, getPlayerIId());
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_AUDIO);
+ }
}
/**
diff --git a/media/jni/soundpool/SoundPool.cpp b/media/jni/soundpool/SoundPool.cpp
index 9f32a83..4c0b8d0 100644
--- a/media/jni/soundpool/SoundPool.cpp
+++ b/media/jni/soundpool/SoundPool.cpp
@@ -21,6 +21,9 @@
#include <algorithm>
#include <thread>
+#define ATRACE_TAG ATRACE_TAG_AUDIO
+#include <utils/Trace.h>
+
#include "SoundPool.h"
namespace android
@@ -135,8 +138,10 @@
return 0;
}
+ ATRACE_BEGIN("SoundPool::play (native)");
const int32_t streamID = mStreamManager.queueForPlay(
sound, soundID, leftVolume, rightVolume, priority, loop, rate, playerIId);
+ ATRACE_END();
ALOGV("%s returned %d", __func__, streamID);
return streamID;
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 02e8cd6..7974f92 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1313,6 +1313,16 @@
}
flag {
+ name: "sim_pin_bouncer_reset"
+ namespace: "systemui"
+ description: "The SIM PIN bouncer does not close after unlocking"
+ bug: "297461589"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "use_transitions_for_keyguard_occluded"
namespace: "systemui"
description: "Use Keyguard Transitions to set Notification Shade occlusion state"
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
index 4674d6e..c01396a 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/windowsizeclass/WindowSizeClass.kt
@@ -16,15 +16,16 @@
package com.android.compose.windowsizeclass
+import android.view.WindowManager
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.toComposeRect
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
-import androidx.window.layout.WindowMetricsCalculator
val LocalWindowSizeClass =
staticCompositionLocalOf<WindowSizeClass> {
@@ -41,7 +42,10 @@
LocalConfiguration.current
val density = LocalDensity.current
val context = LocalContext.current
- val metrics = WindowMetricsCalculator.getOrCreate().computeCurrentWindowMetrics(context)
+ val metrics =
+ remember(context) {
+ context.getSystemService(WindowManager::class.java)!!.currentWindowMetrics
+ }
val size = with(density) { metrics.bounds.toComposeRect().size.toDpSize() }
return WindowSizeClass.calculateFromSize(size)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 502dbe3..5ed11ad 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -34,6 +34,7 @@
import com.android.systemui.plugins.clocks.ClockFaceController
import com.android.systemui.plugins.clocks.ClockFaceEvents
import com.android.systemui.plugins.clocks.ClockMessageBuffers
+import com.android.systemui.plugins.clocks.ClockReactiveSetting
import com.android.systemui.plugins.clocks.ClockSettings
import com.android.systemui.plugins.clocks.DefaultClockFaceLayout
import com.android.systemui.plugins.clocks.WeatherData
@@ -73,7 +74,7 @@
ClockConfig(
DEFAULT_CLOCK_ID,
resources.getString(R.string.clock_default_name),
- resources.getString(R.string.clock_default_description)
+ resources.getString(R.string.clock_default_description),
)
}
@@ -84,14 +85,14 @@
layoutInflater.inflate(R.layout.clock_default_small, parent, false)
as AnimatableClockView,
settings?.seedColor,
- messageBuffers?.smallClockMessageBuffer
+ messageBuffers?.smallClockMessageBuffer,
)
largeClock =
LargeClockFaceController(
layoutInflater.inflate(R.layout.clock_default_large, parent, false)
as AnimatableClockView,
settings?.seedColor,
- messageBuffers?.largeClockMessageBuffer
+ messageBuffers?.largeClockMessageBuffer,
)
clocks = listOf(smallClock.view, largeClock.view)
@@ -272,8 +273,12 @@
}
override fun onWeatherDataChanged(data: WeatherData) {}
+
override fun onAlarmDataChanged(data: AlarmData) {}
+
override fun onZenDataChanged(data: ZenData) {}
+
+ override fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>) {}
}
open inner class DefaultClockAnimations(
@@ -340,10 +345,9 @@
}
}
- class AnimationState(
- var fraction: Float,
- ) {
+ class AnimationState(var fraction: Float) {
var isActive: Boolean = fraction > 0.5f
+
fun update(newFraction: Float): Pair<Boolean, Boolean> {
if (newFraction == fraction) {
return Pair(isActive, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
new file mode 100644
index 0000000..b144f06
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiStateTest.kt
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.panels.ui.viewmodel
+
+import android.content.res.Resources
+import android.content.res.mainResources
+import android.service.quicksettings.Tile
+import android.widget.Button
+import android.widget.Switch
+import androidx.compose.ui.semantics.Role
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.res.R
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TileUiStateTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+ private val resources: Resources
+ get() = kosmos.mainResources
+
+ @Test
+ fun stateUnavailable_secondaryLabelNotmodified() {
+ val testString = "TEST STRING"
+ val state =
+ QSTile.State().apply {
+ state = Tile.STATE_UNAVAILABLE
+ secondaryLabel = testString
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun accessibilityRole_switch() {
+ val stateSwitch =
+ QSTile.State().apply { expandedAccessibilityClassName = Switch::class.java.name }
+ val uiState = stateSwitch.toUiState()
+ assertThat(uiState.accessibilityRole).isEqualTo(Role.Switch)
+ }
+
+ @Test
+ fun accessibilityRole_button() {
+ val stateButton =
+ QSTile.State().apply { expandedAccessibilityClassName = Button::class.java.name }
+ val uiState = stateButton.toUiState()
+ assertThat(uiState.accessibilityRole).isEqualTo(Role.Button)
+ }
+
+ @Test
+ fun accessibilityRole_switchWithSecondaryClick() {
+ val stateSwitchWithSecondaryClick =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Switch::class.java.name
+ handlesSecondaryClick = true
+ }
+ val uiState = stateSwitchWithSecondaryClick.toUiState()
+ assertThat(uiState.accessibilityRole).isEqualTo(Role.Button)
+ }
+
+ @Test
+ fun switchInactive_secondaryLabelNotModified() {
+ val testString = "TEST STRING"
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Switch::class.java.name
+ state = Tile.STATE_INACTIVE
+ secondaryLabel = testString
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEqualTo(testString)
+ }
+
+ @Test
+ fun switchActive_secondaryLabelNotModified() {
+ val testString = "TEST STRING"
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Switch::class.java.name
+ state = Tile.STATE_ACTIVE
+ secondaryLabel = testString
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEqualTo(testString)
+ }
+
+ @Test
+ fun buttonInactive_secondaryLabelNotModifiedWhenEmpty() {
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Button::class.java.name
+ state = Tile.STATE_INACTIVE
+ secondaryLabel = ""
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEmpty()
+ }
+
+ @Test
+ fun buttonActive_secondaryLabelNotModifiedWhenEmpty() {
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Button::class.java.name
+ state = Tile.STATE_ACTIVE
+ secondaryLabel = ""
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEmpty()
+ }
+
+ @Test
+ fun buttonUnavailable_emptySecondaryLabel_default() {
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Button::class.java.name
+ state = Tile.STATE_UNAVAILABLE
+ secondaryLabel = ""
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.tile_unavailable))
+ }
+
+ @Test
+ fun switchUnavailable_emptySecondaryLabel_defaultUnavailable() {
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Switch::class.java.name
+ state = Tile.STATE_UNAVAILABLE
+ secondaryLabel = ""
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.tile_unavailable))
+ }
+
+ @Test
+ fun switchInactive_emptySecondaryLabel_defaultOff() {
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Switch::class.java.name
+ state = Tile.STATE_INACTIVE
+ secondaryLabel = ""
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.switch_bar_off))
+ }
+
+ @Test
+ fun switchActive_emptySecondaryLabel_defaultOn() {
+ val state =
+ QSTile.State().apply {
+ expandedAccessibilityClassName = Switch::class.java.name
+ state = Tile.STATE_ACTIVE
+ secondaryLabel = ""
+ }
+
+ val uiState = state.toUiState()
+
+ assertThat(uiState.secondaryLabel).isEqualTo(resources.getString(R.string.switch_bar_on))
+ }
+
+ @Test
+ fun disabledByPolicy_inactive_appearsAsUnavailable() {
+ val stateDisabledByPolicy =
+ QSTile.State().apply {
+ state = Tile.STATE_INACTIVE
+ disabledByPolicy = true
+ }
+
+ val uiState = stateDisabledByPolicy.toUiState()
+
+ assertThat(uiState.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun disabledByPolicy_active_appearsAsUnavailable() {
+ val stateDisabledByPolicy =
+ QSTile.State().apply {
+ state = Tile.STATE_ACTIVE
+ disabledByPolicy = true
+ }
+
+ val uiState = stateDisabledByPolicy.toUiState()
+
+ assertThat(uiState.state).isEqualTo(Tile.STATE_UNAVAILABLE)
+ }
+
+ @Test
+ fun disabledByPolicy_clickLabel() {
+ val stateDisabledByPolicy =
+ QSTile.State().apply {
+ state = Tile.STATE_INACTIVE
+ disabledByPolicy = true
+ }
+
+ val uiState = stateDisabledByPolicy.toUiState()
+ assertThat(uiState.accessibilityUiState.clickLabel)
+ .isEqualTo(
+ resources.getString(
+ R.string.accessibility_tile_disabled_by_policy_action_description
+ )
+ )
+ }
+
+ @Test
+ fun notDisabledByPolicy_clickLabel_null() {
+ val stateDisabledByPolicy =
+ QSTile.State().apply {
+ state = Tile.STATE_INACTIVE
+ disabledByPolicy = false
+ }
+
+ val uiState = stateDisabledByPolicy.toUiState()
+ assertThat(uiState.accessibilityUiState.clickLabel).isNull()
+ }
+
+ @Test
+ fun disabledByPolicy_unavailableInStateDescription() {
+ val state =
+ QSTile.State().apply {
+ disabledByPolicy = true
+ state = Tile.STATE_INACTIVE
+ }
+
+ val uiState = state.toUiState()
+ assertThat(uiState.accessibilityUiState.stateDescription)
+ .contains(resources.getString(R.string.tile_unavailable))
+ }
+
+ private fun QSTile.State.toUiState() = toUiState(resources)
+}
+
+private val TileUiState.accessibilityRole: Role
+ get() = accessibilityUiState.accessibilityRole
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyCallbackTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/telephony/TelephonyListenerManagerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/temporarydisplay/TemporaryViewUiEventLoggerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/data/repository/TouchpadRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/BackGestureMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/FakeMotionEvent.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/HomeGestureMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/RecentAppsGestureMonitorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilder.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureBuilderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadGestureHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/tracing/TraceUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/tracing/TraceUtilsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/tracing/TraceUtilsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldLightRevealOverlayAnimationTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateLoggingProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/FoldStateRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTestUtilsKosmos.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/ResourceUnfoldTransitionConfigTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/config/TestUnfoldTransitionConfig.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/RemoteUnfoldTransitionReceiverTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/progress/TestUnfoldProgressListener.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceFoldStateProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/DeviceStateRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/updates/RotationChangeProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/TestFoldStateProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/SelectedUserInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/DeviceConfigProxyFakeTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/FakeSharedPreferencesTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/FloatingContentCoordinatorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/RingerModeLiveDataTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/WallpaperControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/WallpaperControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/animation/AnimationUtilTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MessageRouterImplTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/MockExecutorHandlerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/concurrency/RepeatableExecutorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/condition/ConditionalCoreStartableTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/drawable/LoopedAnimatable2DrawableWrapperTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/icons/AppCategoryIconProviderTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowProvider.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/FlowProvider.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/FlowProvider.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/IpcSerializerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/kotlin/SuspendUtilTests.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/LeakReporterTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/leak/LeakReporterTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/leak/LeakReporterTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ref/GcWeakReference.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/ref/GcWeakReference.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/ref/GcWeakReference.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/PostureDependentProximitySensorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplDualTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/sensors/ProximitySensorImplSingleTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/ObservableServiceConnectionTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/PackageObserverTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/service/PackageObserverTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/service/PackageObserverTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/FakeSettingsTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/FakeSettingsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/settings/repository/UserAwareSecureSettingsRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/ui/AnimatedValueTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/view/ViewUtilTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/view/ViewUtilTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/SettableWakeLockTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/util/wakelock/WakeLockTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/util/wakelock/WakeLockTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/VolumeControllerAdapterTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/data/repository/VolumeDialogRepositoryTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/VolumeDialogInteractorTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsControllerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallet/util/WalletCardUtilsTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/WallpaperLocalColorExtractorTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wallpapers/data/repository/FakeWallpaperRepository.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubblesTestActivity.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTestActivity.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/BubblesTestActivity.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/SyncExecutor.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/SyncExecutor.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/SyncExecutor.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubbleController.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubbleController.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
similarity index 100%
rename from packages/SystemUI/tests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/wmshell/TestableBubblePositioner.java
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
index 4812ff0..8dc4815 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt
@@ -69,11 +69,7 @@
val events: ClockEvents
/** Initializes various rendering parameters. If never called, provides reasonable defaults. */
- fun initialize(
- resources: Resources,
- dozeFraction: Float,
- foldFraction: Float,
- )
+ fun initialize(resources: Resources, dozeFraction: Float, foldFraction: Float)
/** Optional method for dumping debug information */
fun dump(pw: PrintWriter)
@@ -109,11 +105,7 @@
val largeClockMessageBuffer: MessageBuffer,
)
-data class AodClockBurnInModel(
- val scale: Float,
- val translationX: Float,
- val translationY: Float,
-)
+data class AodClockBurnInModel(val scale: Float, val translationX: Float, val translationY: Float)
/** Specifies layout information for the */
interface ClockFaceLayout {
@@ -180,8 +172,20 @@
/** Call with zen/dnd information */
fun onZenDataChanged(data: ZenData)
+
+ /** Update reactive axes for this clock */
+ fun onReactiveAxesChanged(axes: List<ClockReactiveSetting>)
}
+/** Axis setting value for a clock */
+data class ClockReactiveSetting(
+ /** Axis key; matches ClockReactiveAxis.key */
+ val key: String,
+
+ /** Value to set this axis to */
+ val value: Float,
+)
+
/** Methods which trigger various clock animations */
interface ClockAnimations {
/** Runs an enter animation (if any) */
@@ -264,9 +268,7 @@
}
/** Some data about a clock design */
-data class ClockMetadata(
- val clockId: ClockId,
-)
+data class ClockMetadata(val clockId: ClockId)
data class ClockPickerConfig(
val id: String,
@@ -283,10 +285,46 @@
/** True if the clock will react to tone changes in the seed color */
val isReactiveToTone: Boolean = true,
- /** True if the clock is capable of chagning style in reaction to touches */
+ /** True if the clock is capable of changing style in reaction to touches */
val isReactiveToTouch: Boolean = false,
+
+ /** Font axes that can be modified on this clock */
+ val axes: List<ClockReactiveAxis> = listOf(),
)
+/** Represents an Axis that can be modified */
+data class ClockReactiveAxis(
+ /** Axis key, not user renderable */
+ val key: String,
+
+ /** Intended mode of user interaction */
+ val type: AxisType,
+
+ /** Maximum value the axis supports */
+ val maxValue: Float,
+
+ /** Minimum value the axis supports */
+ val minValue: Float,
+
+ /** Current value the axis is set to */
+ val currentValue: Float,
+
+ /** User-renderable name of the axis */
+ val name: String,
+
+ /** Description of the axis */
+ val description: String,
+)
+
+/** Axis user interaction modes */
+enum class AxisType {
+ /** Boolean toggle. Swaps between minValue & maxValue */
+ Toggle,
+
+ /** Continuous slider between minValue & maxValue */
+ Slider,
+}
+
/** Render configuration for the full clock. Modifies the way systemUI behaves with this clock. */
data class ClockConfig(
val id: String,
@@ -300,7 +338,8 @@
/** Transition to AOD should move smartspace like large clock instead of small clock */
val useAlternateSmartspaceAODTransition: Boolean = false,
- @Deprecated("TODO(b/352049256): Remove")
+ /** Use ClockPickerConfig.isReactiveToTone instead */
+ @Deprecated("TODO(b/352049256): Remove in favor of ClockPickerConfig.isReactiveToTone")
val isReactiveToTone: Boolean = true,
/** True if the clock is large frame clock, which will use weather in compose. */
@@ -331,6 +370,7 @@
data class ClockSettings(
val clockId: ClockId? = null,
val seedColor: Int? = null,
+ val axes: List<ClockReactiveSetting>? = null,
) {
// Exclude metadata from equality checks
var metadata: JSONObject = JSONObject()
@@ -345,6 +385,8 @@
return ""
}
+ // TODO(b/364673977): Serialize axes
+
return JSONObject()
.put(KEY_CLOCK_ID, setting.clockId)
.put(KEY_SEED_COLOR, setting.seedColor)
@@ -357,11 +399,13 @@
return null
}
+ // TODO(b/364673977): Deserialize axes
+
val json = JSONObject(jsonStr)
val result =
ClockSettings(
if (!json.isNull(KEY_CLOCK_ID)) json.getString(KEY_CLOCK_ID) else null,
- if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null
+ if (!json.isNull(KEY_SEED_COLOR)) json.getInt(KEY_SEED_COLOR) else null,
)
if (!json.isNull(KEY_METADATA)) {
result.metadata = json.getJSONObject(KEY_METADATA)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
index a5b62b6..b16c683 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardConstants.java
@@ -28,5 +28,4 @@
* be used temporarily for debugging.
*/
public static final boolean DEBUG = Log.isLoggable("Keyguard", Log.DEBUG);
- public static final boolean DEBUG_SIM_STATES = true;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 1c1acf8..52c93f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -53,7 +53,6 @@
extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
public static final String TAG = "KeyguardSimPinView";
private static final String LOG_TAG = "KeyguardSimPinView";
- private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final TelephonyManager mTelephonyManager;
@@ -71,7 +70,7 @@
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
// If subId has gone to PUK required then we need to go to the PUK screen.
if (subId == mSubId && simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
getKeyguardSecurityCallback().showCurrentSecurityScreen();
@@ -129,7 +128,7 @@
@Override
void resetState() {
super.resetState();
- if (DEBUG) Log.v(TAG, "Resetting state");
+ Log.v(TAG, "Resetting state");
handleSubInfoChangeIfNeeded();
mMessageAreaController.setMessage("");
if (mShowDefaultMessage) {
@@ -216,11 +215,9 @@
mMessageAreaController.setMessage(mView.getResources().getString(
R.string.kg_password_pin_failed));
}
- if (DEBUG) {
- Log.d(LOG_TAG, "verifyPasswordAndUnlock "
- + " CheckSimPin.onSimCheckResponse: " + result
- + " attemptsRemaining=" + result.getAttemptsRemaining());
- }
+ Log.d(LOG_TAG, "verifyPasswordAndUnlock "
+ + " CheckSimPin.onSimCheckResponse: " + result
+ + " attemptsRemaining=" + result.getAttemptsRemaining());
}
getKeyguardSecurityCallback().userActivity();
mCheckSimPinThread = null;
@@ -280,10 +277,8 @@
displayMessage = mView.getResources()
.getString(R.string.kg_sim_lock_esim_instructions, displayMessage);
}
- if (DEBUG) {
- Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
- + attemptsRemaining + " displayMessage=" + displayMessage);
- }
+ Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining="
+ + attemptsRemaining + " displayMessage=" + displayMessage);
return displayMessage;
}
@@ -323,14 +318,10 @@
@Override
public void run() {
- if (DEBUG) {
- Log.v(TAG, "call supplyIccLockPin(subid=" + mSubId + ")");
- }
+ Log.v(TAG, "call supplyIccLockPin(subid=" + mSubId + ")");
TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId);
final PinResult result = telephonyManager.supplyIccLockPin(mPin);
- if (DEBUG) {
- Log.v(TAG, "supplyIccLockPin returned: " + result.toString());
- }
+ Log.v(TAG, "supplyIccLockPin returned: " + result.toString());
mView.post(() -> onSimCheckResponse(result));
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f731186..22130f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -43,6 +43,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
+import static com.android.systemui.Flags.simPinBouncerReset;
import static com.android.systemui.statusbar.policy.DevicePostureController.DEVICE_POSTURE_OPENED;
import android.annotation.AnyThread;
@@ -1703,6 +1704,9 @@
intent.getStringExtra(Intent.EXTRA_SIM_STATE),
args.slotId,
args.subId);
+ if (args.slotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
mHandler.obtainMessage(MSG_SIM_STATE_CHANGE, args.subId, args.slotId, args.simState)
.sendToTarget();
} else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) {
@@ -1940,7 +1944,13 @@
}
int state = TelephonyManager.SIM_STATE_UNKNOWN;
String stateExtra = intent.getStringExtra(Intent.EXTRA_SIM_STATE);
- int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+
+ int defaultSlotId = 0;
+ if (simPinBouncerReset()) {
+ defaultSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ }
+ int slotId = intent.getIntExtra(SubscriptionManager.EXTRA_SLOT_INDEX,
+ defaultSlotId);
int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
if (Intent.SIM_STATE_ABSENT.equals(stateExtra)) {
@@ -2479,6 +2489,12 @@
this::onTransitionStateChanged
);
}
+
+ // start() can be invoked in the middle of user switching, so check for this state and issue
+ // the call manually as that important event was missed.
+ if (mUserTracker.isUserSwitching()) {
+ handleUserSwitching(mUserTracker.getUserId(), () -> {});
+ }
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
index 81ea2e7..62720a5 100644
--- a/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/ui/viewmodel/ConnectingDisplayViewModel.kt
@@ -79,7 +79,7 @@
.combine(concurrentDisplaysInProgessFlow) { pendingDisplay, concurrentDisplaysInProgress
->
if (pendingDisplay == null) {
- hideDialog()
+ dismissDialog()
} else {
showDialog(pendingDisplay, concurrentDisplaysInProgress)
}
@@ -88,17 +88,17 @@
}
private fun showDialog(pendingDisplay: PendingDisplay, concurrentDisplaysInProgess: Boolean) {
- hideDialog()
+ dismissDialog()
dialog =
bottomSheetFactory
.createDialog(
onStartMirroringClickListener = {
scope.launch(bgDispatcher) { pendingDisplay.enable() }
- hideDialog()
+ dismissDialog()
},
onCancelMirroring = {
scope.launch(bgDispatcher) { pendingDisplay.ignore() }
- hideDialog()
+ dismissDialog()
},
navbarBottomInsetsProvider = { Utils.getNavbarInsets(context).bottom },
showConcurrentDisplayInfo = concurrentDisplaysInProgess
@@ -106,8 +106,8 @@
.apply { show() }
}
- private fun hideDialog() {
- dialog?.hide()
+ private fun dismissDialog() {
+ dialog?.dismiss()
dialog = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
index 342325f..6df8355 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/CustomizationProvider.kt
@@ -54,29 +54,25 @@
addURI(
Contract.AUTHORITY,
Contract.LockScreenQuickAffordances.qualifiedTablePath(
- Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
),
MATCH_CODE_ALL_SLOTS,
)
addURI(
Contract.AUTHORITY,
Contract.LockScreenQuickAffordances.qualifiedTablePath(
- Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
),
MATCH_CODE_ALL_AFFORDANCES,
)
addURI(
Contract.AUTHORITY,
Contract.LockScreenQuickAffordances.qualifiedTablePath(
- Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
),
MATCH_CODE_ALL_SELECTIONS,
)
- addURI(
- Contract.AUTHORITY,
- Contract.FlagsTable.TABLE_NAME,
- MATCH_CODE_ALL_FLAGS,
- )
+ addURI(Contract.AUTHORITY, Contract.FlagsTable.TABLE_NAME, MATCH_CODE_ALL_FLAGS)
}
override fun onCreate(): Boolean {
@@ -106,15 +102,15 @@
when (uriMatcher.match(uri)) {
MATCH_CODE_ALL_SLOTS ->
Contract.LockScreenQuickAffordances.qualifiedTablePath(
- Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.SlotTable.TABLE_NAME
)
MATCH_CODE_ALL_AFFORDANCES ->
Contract.LockScreenQuickAffordances.qualifiedTablePath(
- Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.AffordanceTable.TABLE_NAME
)
MATCH_CODE_ALL_SELECTIONS ->
Contract.LockScreenQuickAffordances.qualifiedTablePath(
- Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME,
+ Contract.LockScreenQuickAffordances.SelectionTable.TABLE_NAME
)
MATCH_CODE_ALL_FLAGS -> Contract.FlagsTable.TABLE_NAME
else -> null
@@ -128,6 +124,7 @@
}
override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ if (!::mainDispatcher.isInitialized) return null
if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
throw UnsupportedOperationException()
}
@@ -142,6 +139,7 @@
selectionArgs: Array<out String>?,
sortOrder: String?,
): Cursor? {
+ if (!::mainDispatcher.isInitialized) return null
return runBlocking("$TAG#query", mainDispatcher) {
when (uriMatcher.match(uri)) {
MATCH_CODE_ALL_AFFORDANCES -> queryAffordances()
@@ -163,11 +161,8 @@
return 0
}
- override fun delete(
- uri: Uri,
- selection: String?,
- selectionArgs: Array<out String>?,
- ): Int {
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
+ if (!::mainDispatcher.isInitialized) return 0
if (uriMatcher.match(uri) != MATCH_CODE_ALL_SELECTIONS) {
throw UnsupportedOperationException()
}
@@ -232,11 +227,7 @@
throw IllegalArgumentException("Cannot insert selection, affordance ID was empty!")
}
- val success =
- interactor.select(
- slotId = slotId,
- affordanceId = affordanceId,
- )
+ val success = interactor.select(slotId = slotId, affordanceId = affordanceId)
return if (success) {
Log.d(TAG, "Successfully selected $affordanceId for slot $slotId")
@@ -318,22 +309,14 @@
)
.apply {
interactor.getSlotPickerRepresentations().forEach { representation ->
- addRow(
- arrayOf(
- representation.id,
- representation.maxSelectedAffordances,
- )
- )
+ addRow(arrayOf(representation.id, representation.maxSelectedAffordances))
}
}
}
private suspend fun queryFlags(): Cursor {
return MatrixCursor(
- arrayOf(
- Contract.FlagsTable.Columns.NAME,
- Contract.FlagsTable.Columns.VALUE,
- )
+ arrayOf(Contract.FlagsTable.Columns.NAME, Contract.FlagsTable.Columns.VALUE)
)
.apply {
interactor.getPickerFlags().forEach { flag ->
@@ -351,10 +334,7 @@
}
}
- private suspend fun deleteSelection(
- uri: Uri,
- selectionArgs: Array<out String>?,
- ): Int {
+ private suspend fun deleteSelection(uri: Uri, selectionArgs: Array<out String>?): Int {
if (selectionArgs == null) {
throw IllegalArgumentException(
"Cannot delete selection, selection arguments not included!"
@@ -372,11 +352,7 @@
)
}
- val deleted =
- interactor.unselect(
- slotId = slotId,
- affordanceId = affordanceId,
- )
+ val deleted = interactor.unselect(slotId = slotId, affordanceId = affordanceId)
return if (deleted) {
Log.d(TAG, "Successfully unselected $affordanceId for slot $slotId")
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 3b1569d..1a0525d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -42,6 +42,7 @@
import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.Flags.notifyPowerManagerUserActivityBackground;
import static com.android.systemui.Flags.relockWithPowerButtonImmediately;
+import static com.android.systemui.Flags.simPinBouncerReset;
import static com.android.systemui.Flags.translucentOccludingActivityFix;
import static com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel.DREAMING_ANIMATION_DURATION_MS;
@@ -238,7 +239,6 @@
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
private static final boolean DEBUG = KeyguardConstants.DEBUG;
- private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
private final static String TAG = "KeyguardViewMediator";
@@ -649,11 +649,8 @@
@Override
public void onSimStateChanged(int subId, int slotId, int simState) {
-
- if (DEBUG_SIM_STATES) {
- Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
- + ",state=" + simState + ")");
- }
+ Log.d(TAG, "onSimStateChanged(subId=" + subId + ", slotId=" + slotId
+ + ",state=" + TelephonyManager.simStateToString(simState) + ")");
int size = mKeyguardStateCallbacks.size();
boolean simPinSecure = mUpdateMonitor.isSimPinSecure();
@@ -686,7 +683,7 @@
synchronized (KeyguardViewMediator.this) {
if (shouldWaitForProvisioning()) {
if (!mShowing) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "ICC_ABSENT isn't showing,"
+ Log.d(TAG, "ICC_ABSENT isn't showing,"
+ " we need to show the keyguard since the "
+ "device isn't provisioned yet.");
doKeyguardLocked(null);
@@ -698,11 +695,21 @@
// MVNO SIMs can become transiently NOT_READY when switching networks,
// so we should only lock when they are ABSENT.
if (lastSimStateWasLocked) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to ABSENT when the "
+ Log.d(TAG, "SIM moved to ABSENT when the "
+ "previous state was locked. Reset the state.");
resetStateLocked();
}
mSimWasLocked.append(slotId, false);
+ } else if (simState == TelephonyManager.SIM_STATE_NOT_READY) {
+ if (simPinBouncerReset()) {
+ // Support eSIM disablement, and do not clear `mSimWasLocked`.
+ // NOT_READY could just be a temporary state
+ if (lastSimStateWasLocked) {
+ Log.d(TAG, "SIM moved to NOT_READY when the "
+ + "previous state was locked. Reset the state.");
+ resetStateLocked();
+ }
+ }
}
}
break;
@@ -712,7 +719,7 @@
mSimWasLocked.append(slotId, true);
mPendingPinLock = true;
if (!mShowing) {
- if (DEBUG_SIM_STATES) Log.d(TAG,
+ Log.d(TAG,
"INTENT_VALUE_ICC_LOCKED and keygaurd isn't "
+ "showing; need to show keyguard so user can enter sim pin");
doKeyguardLocked(null);
@@ -724,11 +731,11 @@
case TelephonyManager.SIM_STATE_PERM_DISABLED:
synchronized (KeyguardViewMediator.this) {
if (!mShowing) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED and "
+ Log.d(TAG, "PERM_DISABLED and "
+ "keygaurd isn't showing.");
doKeyguardLocked(null);
} else {
- if (DEBUG_SIM_STATES) Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ Log.d(TAG, "PERM_DISABLED, resetStateLocked to"
+ "show permanently disabled message in lockscreen.");
resetStateLocked();
}
@@ -736,9 +743,9 @@
break;
case TelephonyManager.SIM_STATE_READY:
synchronized (KeyguardViewMediator.this) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "READY, reset state? " + mShowing);
+ Log.d(TAG, "READY, reset state? " + mShowing);
if (mShowing && mSimWasLocked.get(slotId, false)) {
- if (DEBUG_SIM_STATES) Log.d(TAG, "SIM moved to READY when the "
+ Log.d(TAG, "SIM moved to READY when the "
+ "previously was locked. Reset the state.");
mSimWasLocked.append(slotId, false);
resetStateLocked();
@@ -746,7 +753,7 @@
}
break;
default:
- if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
+ Log.v(TAG, "Unspecific state: " + simState);
break;
}
}
@@ -1682,6 +1689,12 @@
});
mJavaAdapter.alwaysCollectFlow(communalViewModel.getTransitionFromOccludedEnded(),
getFinishedCallbackConsumer());
+
+ // System ready can be invoked in the middle of user switching, so check for this state
+ // and issue the call manually as that important event was missed.
+ if (mUserTracker.isUserSwitching()) {
+ mUpdateCallback.onUserSwitching(mUserTracker.getUserId());
+ }
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
index d7e6bdb..4457f1d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt
@@ -23,10 +23,12 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.KeyguardWmStateRefactor
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.TrustRepository
import com.android.systemui.keyguard.shared.model.DismissAction
import com.android.systemui.keyguard.shared.model.KeyguardDone
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
@@ -35,6 +37,7 @@
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
@@ -56,6 +59,7 @@
trustRepository: TrustRepository,
alternateBouncerInteractor: AlternateBouncerInteractor,
powerInteractor: PowerInteractor,
+ keyguardTransitionInteractor: KeyguardTransitionInteractor,
) {
/*
* Updates when a biometric has authenticated the device and is requesting to dismiss
@@ -76,9 +80,9 @@
primaryBouncerInteractor.isShowing,
alternateBouncerInteractor.isVisible,
powerInteractor.isInteractive,
- ::Triple
+ ::Triple,
),
- ::toQuad
+ ::toQuad,
)
.filter { (trustModel, primaryBouncerShowing, altBouncerShowing, interactive) ->
val bouncerShowing = primaryBouncerShowing || altBouncerShowing
@@ -144,9 +148,7 @@
*
* TODO(b/358412565): Support dismiss messages.
*/
- fun dismissKeyguardWithCallback(
- callback: IKeyguardDismissCallback?,
- ) {
+ fun dismissKeyguardWithCallback(callback: IKeyguardDismissCallback?) {
scope.launch {
withContext(mainDispatcher) {
if (callback != null) {
@@ -163,4 +165,14 @@
}
}
}
+
+ init {
+ if (KeyguardWmStateRefactor.isEnabled) {
+ scope.launch {
+ keyguardTransitionInteractor.currentKeyguardState
+ .filter { it == KeyguardState.GONE }
+ .collect { dismissCallbackRegistry.notifyDismissSucceeded() }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
index c2f1c3d..af167d4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -158,7 +158,7 @@
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
- savedInstanceState: Bundle?
+ savedInstanceState: Bundle?,
): View {
val context = inflater.context
return ComposeView(context).apply {
@@ -181,7 +181,7 @@
notificationScrimClippingParams.bottom,
notificationScrimClippingParams.radius,
)
- }
+ },
) {
AnimatedContent(targetState = qsState) {
when (it) {
@@ -272,7 +272,7 @@
qsExpansionFraction: Float,
panelExpansionFraction: Float,
headerTranslation: Float,
- squishinessFraction: Float
+ squishinessFraction: Float,
) {
viewModel.qsExpansionValue = qsExpansionFraction
viewModel.panelExpansionFractionValue = panelExpansionFraction
@@ -318,12 +318,12 @@
override fun setTransitionToFullShadeProgress(
isTransitioningToFullShade: Boolean,
qsTransitionFraction: Float,
- qsSquishinessFraction: Float
+ qsSquishinessFraction: Float,
) {
super.setTransitionToFullShadeProgress(
isTransitioningToFullShade,
qsTransitionFraction,
- qsSquishinessFraction
+ qsSquishinessFraction,
)
}
@@ -334,7 +334,7 @@
bottom: Int,
cornerRadius: Int,
visible: Boolean,
- fullWidth: Boolean
+ fullWidth: Boolean,
) {
notificationScrimClippingParams.isEnabled = visible
notificationScrimClippingParams.top = top
@@ -402,7 +402,7 @@
launch {
setListenerJob(
heightListener,
- viewModel.containerViewModel.editModeViewModel.isEditing
+ viewModel.containerViewModel.editModeViewModel.isEditing,
) {
onQsHeightChanged()
}
@@ -410,7 +410,7 @@
launch {
setListenerJob(
qsContainerController,
- viewModel.containerViewModel.editModeViewModel.isEditing
+ viewModel.containerViewModel.editModeViewModel.isEditing,
) {
setCustomizerShowing(it)
}
@@ -422,6 +422,7 @@
@Composable
private fun QuickQuickSettingsElement() {
val qqsPadding by viewModel.qqsHeaderHeight.collectAsStateWithLifecycle()
+ val bottomPadding = dimensionResource(id = R.dimen.qqs_layout_padding_bottom)
DisposableEffect(Unit) {
qqsVisible.value = true
@@ -441,7 +442,7 @@
)
}
.onSizeChanged { size -> qqsHeight.value = size.height }
- .padding(top = { qqsPadding })
+ .padding(top = { qqsPadding }, bottom = { bottomPadding.roundToPx() })
) {
val qsEnabled by viewModel.qsEnabled.collectAsStateWithLifecycle()
if (qsEnabled) {
@@ -450,7 +451,7 @@
modifier =
Modifier.collapseExpandSemanticAction(
stringResource(id = R.string.accessibility_quick_settings_expand)
- )
+ ),
)
}
}
@@ -482,7 +483,7 @@
FooterActions(
viewModel = viewModel.footerActionsViewModel,
qsVisibilityLifecycleOwner = this@QSFragmentCompose,
- modifier = Modifier.sysuiResTag("qs_footer_actions")
+ modifier = Modifier.sysuiResTag("qs_footer_actions"),
)
}
}
@@ -562,7 +563,7 @@
private suspend inline fun <Listener : Any, Data> setListenerJob(
listenerFlow: MutableStateFlow<Listener?>,
dataFlow: Flow<Data>,
- crossinline onCollect: suspend Listener.(Data) -> Unit
+ crossinline onCollect: suspend Listener.(Data) -> Unit,
) {
coroutineScope {
try {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
index eeb55ca..fde40da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/QuickQuickSettings.kt
@@ -22,18 +22,16 @@
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.compose.ui.util.fastMap
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.qs.panels.ui.viewmodel.QuickQuickSettingsViewModel
@Composable
-fun QuickQuickSettings(
- viewModel: QuickQuickSettingsViewModel,
- modifier: Modifier = Modifier,
-) {
+fun QuickQuickSettings(viewModel: QuickQuickSettingsViewModel, modifier: Modifier = Modifier) {
val sizedTiles by
viewModel.tileViewModels.collectAsStateWithLifecycle(initialValue = emptyList())
- val tiles = sizedTiles.map { it.tile }
+ val tiles = sizedTiles.fastMap { it.tile }
DisposableEffect(tiles) {
val token = Any()
@@ -44,14 +42,18 @@
TileLazyGrid(
modifier = modifier.sysuiResTag("qqs_tile_layout"),
- columns = GridCells.Fixed(columns)
+ columns = GridCells.Fixed(columns),
) {
items(
- tiles.size,
+ sizedTiles.size,
key = { index -> sizedTiles[index].tile.spec.spec },
- span = { index -> GridItemSpan(sizedTiles[index].width) }
+ span = { index -> GridItemSpan(sizedTiles[index].width) },
) { index ->
- Tile(tile = tiles[index], iconOnly = sizedTiles[index].isIcon, modifier = Modifier)
+ Tile(
+ tile = sizedTiles[index].tile,
+ iconOnly = sizedTiles[index].isIcon,
+ modifier = Modifier,
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
index 93037d1..afd47a7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/Tile.kt
@@ -18,6 +18,7 @@
package com.android.systemui.qs.panels.ui.compose
+import android.content.res.Resources
import android.graphics.drawable.Animatable
import android.service.quicksettings.Tile.STATE_ACTIVE
import android.service.quicksettings.Tile.STATE_INACTIVE
@@ -71,6 +72,7 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -85,13 +87,19 @@
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInRoot
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.semantics.clearAndSetSemantics
+import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.role
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.stateDescription
+import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
@@ -106,12 +114,15 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.ui.compose.Icon
import com.android.systemui.common.ui.compose.load
+import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.qs.panels.shared.model.SizedTile
import com.android.systemui.qs.panels.shared.model.SizedTileImpl
+import com.android.systemui.qs.panels.ui.compose.TileDefaults.longPressLabel
import com.android.systemui.qs.panels.ui.model.GridCell
import com.android.systemui.qs.panels.ui.model.SpacerGridCell
import com.android.systemui.qs.panels.ui.model.TileGridCell
+import com.android.systemui.qs.panels.ui.viewmodel.AccessibilityUiState
import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel
import com.android.systemui.qs.panels.ui.viewmodel.TileUiState
import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel
@@ -126,15 +137,15 @@
object TileType
+private const val TEST_TAG_SMALL = "qs_tile_small"
+private const val TEST_TAG_LARGE = "qs_tile_large"
+private const val TEST_TAG_TOGGLE = "qs_tile_toggle_target"
+
@Composable
-fun Tile(
- tile: TileViewModel,
- iconOnly: Boolean,
- showLabels: Boolean = false,
- modifier: Modifier,
-) {
+fun Tile(tile: TileViewModel, iconOnly: Boolean, showLabels: Boolean = false, modifier: Modifier) {
val state by tile.state.collectAsStateWithLifecycle(tile.currentState)
- val uiState = remember(state) { state.toUiState() }
+ val resources = resources()
+ val uiState = remember(state, resources) { state.toUiState(resources) }
val colors = TileDefaults.getColorForState(uiState)
// TODO(b/361789146): Draw the shapes instead of clipping
@@ -150,6 +161,7 @@
onClick = tile::onClick,
onLongClick = tile::onLongClick,
modifier = modifier.height(tileHeight()),
+ uiState = uiState,
) {
val icon = getTileIcon(icon = uiState.icon)
if (iconOnly) {
@@ -169,6 +181,7 @@
}
},
onLongClick = { tile.onLongClick(it) },
+ accessibilityUiState = uiState.accessibilityUiState,
)
}
}
@@ -185,6 +198,7 @@
onClick: (Expandable) -> Unit = {},
onLongClick: (Expandable) -> Unit = {},
modifier: Modifier = Modifier,
+ uiState: TileUiState? = null,
content: @Composable BoxScope.(Expandable) -> Unit,
) {
Column(
@@ -194,7 +208,7 @@
modifier = modifier,
) {
val backgroundColor =
- if (iconOnly) {
+ if (iconOnly || uiState?.handlesSecondaryClick != true) {
colors.iconBackground
} else {
colors.background
@@ -202,18 +216,43 @@
Expandable(
color = backgroundColor,
shape = shape,
- modifier = Modifier.height(tileHeight()).clip(shape)
+ modifier = Modifier.height(tileHeight()).clip(shape),
) {
+ val longPressLabel = longPressLabel()
Box(
modifier =
Modifier.fillMaxSize()
.thenIf(clickEnabled) {
Modifier.combinedClickable(
onClick = { onClick(it) },
- onLongClick = { onLongClick(it) }
+ onLongClick = { onLongClick(it) },
+ onClickLabel = uiState?.accessibilityUiState?.clickLabel,
+ onLongClickLabel = longPressLabel,
)
}
- .tilePadding(),
+ .thenIf(uiState != null) {
+ uiState as TileUiState
+ Modifier.semantics {
+ role = uiState.accessibilityUiState.accessibilityRole
+ if (
+ uiState.accessibilityUiState.accessibilityRole ==
+ Role.Switch
+ ) {
+ uiState.accessibilityUiState.toggleableState?.let {
+ toggleableState = it
+ }
+ }
+ stateDescription = uiState.accessibilityUiState.stateDescription
+ }
+ .sysuiResTag(if (iconOnly) TEST_TAG_SMALL else TEST_TAG_LARGE)
+ .thenIf(iconOnly) {
+ Modifier.semantics {
+ contentDescription =
+ uiState.accessibilityUiState.contentDescription
+ }
+ }
+ }
+ .tilePadding()
) {
content(it)
}
@@ -238,21 +277,39 @@
icon: Icon,
colors: TileColors,
iconShape: Shape,
+ accessibilityUiState: AccessibilityUiState? = null,
toggleClickSupported: Boolean = false,
onClick: () -> Unit = {},
onLongClick: () -> Unit = {},
) {
Row(
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = tileHorizontalArrangement()
+ horizontalArrangement = tileHorizontalArrangement(),
) {
// Icon
+ val longPressLabel = longPressLabel()
Box(
modifier =
Modifier.size(TileDefaults.ToggleTargetSize).thenIf(toggleClickSupported) {
Modifier.clip(iconShape)
.background(colors.iconBackground, { 1f })
- .combinedClickable(onClick = onClick, onLongClick = onLongClick)
+ .combinedClickable(
+ onClick = onClick,
+ onLongClick = onLongClick,
+ onLongClickLabel = longPressLabel,
+ )
+ .thenIf(accessibilityUiState != null) {
+ accessibilityUiState as AccessibilityUiState
+ Modifier.semantics {
+ contentDescription = accessibilityUiState.contentDescription
+ stateDescription = accessibilityUiState.stateDescription
+ accessibilityUiState.toggleableState?.let {
+ toggleableState = it
+ }
+ role = Role.Switch
+ }
+ .sysuiResTag(TEST_TAG_TOGGLE)
+ }
}
) {
TileIcon(icon = icon, color = colors.icon, modifier = Modifier.align(Alignment.Center))
@@ -260,16 +317,19 @@
// Labels
Column(verticalArrangement = Arrangement.Center, modifier = Modifier.fillMaxHeight()) {
- Text(
- label,
- color = colors.label,
- modifier = Modifier.tileMarquee(),
- )
+ Text(label, color = colors.label, modifier = Modifier.tileMarquee())
if (!TextUtils.isEmpty(secondaryLabel)) {
Text(
secondaryLabel ?: "",
color = colors.secondaryLabel,
- modifier = Modifier.tileMarquee(),
+ modifier =
+ Modifier.tileMarquee().thenIf(
+ accessibilityUiState
+ ?.stateDescription
+ ?.contains(secondaryLabel ?: "") == true
+ ) {
+ Modifier.clearAndSetSemantics {}
+ },
)
}
}
@@ -277,10 +337,7 @@
}
private fun Modifier.tileMarquee(): Modifier {
- return basicMarquee(
- iterations = 1,
- initialDelayMillis = 200,
- )
+ return basicMarquee(iterations = 1, initialDelayMillis = 200)
}
@Composable
@@ -320,11 +377,11 @@
Column(
verticalArrangement =
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState())
+ modifier = modifier.fillMaxSize().verticalScroll(rememberScrollState()),
) {
AnimatedContent(
targetState = currentListState.dragInProgress,
- modifier = Modifier.wrapContentSize()
+ modifier = Modifier.wrapContentSize(),
) { dragIsInProgress ->
EditGridHeader(Modifier.dragAndDropRemoveZone(currentListState, onRemoveTile)) {
if (dragIsInProgress) {
@@ -348,12 +405,12 @@
AnimatedVisibility(
visible = !currentListState.dragInProgress,
enter = fadeIn(),
- exit = fadeOut()
+ exit = fadeOut(),
) {
Column(
verticalArrangement =
spacedBy(dimensionResource(id = R.dimen.qs_label_container_margin)),
- modifier = modifier.fillMaxSize()
+ modifier = modifier.fillMaxSize(),
) {
EditGridHeader { Text(text = "Hold and drag to add tiles.") }
@@ -381,14 +438,14 @@
@Composable
private fun EditGridHeader(
modifier: Modifier = Modifier,
- content: @Composable BoxScope.() -> Unit
+ content: @Composable BoxScope.() -> Unit,
) {
CompositionLocalProvider(
LocalContentColor provides MaterialTheme.colorScheme.onBackground.copy(alpha = .5f)
) {
Box(
contentAlignment = Alignment.Center,
- modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight)
+ modifier = modifier.fillMaxWidth().height(EditModeTileDefaults.EditGridHeaderHeight),
) {
content()
}
@@ -403,7 +460,7 @@
modifier =
Modifier.fillMaxHeight()
.border(1.dp, LocalContentColor.current, shape = CircleShape)
- .padding(10.dp)
+ .padding(10.dp),
) {
Icon(imageVector = Icons.Default.Clear, contentDescription = null)
Text(text = "Remove")
@@ -454,7 +511,7 @@
gridContentOffset = coordinates.positionInRoot()
}
.testTag(CURRENT_TILES_GRID_TEST_TAG),
- columns = GridCells.Fixed(columns)
+ columns = GridCells.Fixed(columns),
) {
editTiles(
listState.tiles,
@@ -488,7 +545,7 @@
// Available tiles
TileLazyGrid(
modifier = Modifier.height(availableGridHeight).testTag(AVAILABLE_TILES_GRID_TEST_TAG),
- columns = GridCells.Fixed(columns)
+ columns = GridCells.Fixed(columns),
) {
groupedTiles.forEach { category, tiles ->
stickyHeader {
@@ -498,7 +555,7 @@
color = labelColors.label,
modifier =
Modifier.background(Color.Black)
- .padding(start = 16.dp, bottom = 8.dp, top = 8.dp)
+ .padding(start = 16.dp, bottom = 8.dp, top = 8.dp),
)
}
editTiles(
@@ -542,7 +599,7 @@
count = cells.size,
key = { cells[it].key(it, dragAndDropState) },
span = { cells[it].span },
- contentType = { TileType }
+ contentType = { TileType },
) { index ->
when (val cell = cells[index]) {
is TileGridCell ->
@@ -552,7 +609,7 @@
Modifier.background(
color = MaterialTheme.colorScheme.secondary,
alpha = { EditModeTileDefaults.PLACEHOLDER_ALPHA },
- shape = RoundedCornerShape(TileDefaults.InactiveCornerRadius)
+ shape = RoundedCornerShape(TileDefaults.InactiveCornerRadius),
)
.animateItem()
)
@@ -565,7 +622,7 @@
onClick = onClick,
onResize = onResize,
showLabels = showLabels,
- indicatePosition = indicatePosition
+ indicatePosition = indicatePosition,
)
}
is SpacerGridCell -> SpacerGridCell()
@@ -604,7 +661,7 @@
modifier =
Modifier.height(tileHeight)
.animateItem()
- .semantics {
+ .semantics(mergeDescendants = true) {
onClick(onClickActionName) { false }
this.stateDescription = stateDescription
}
@@ -613,7 +670,7 @@
onClick,
onResize,
dragAndDropState,
- )
+ ),
)
}
@@ -645,7 +702,7 @@
TileIcon(
icon = tileViewModel.icon,
color = colors.icon,
- modifier = Modifier.align(Alignment.Center)
+ modifier = Modifier.align(Alignment.Center),
)
} else {
LargeTileContent(
@@ -694,11 +751,7 @@
}
}
if (loadedDrawable !is Animatable) {
- Icon(
- icon = icon,
- tint = color,
- modifier = iconModifier,
- )
+ Icon(icon = icon, tint = color, modifier = iconModifier)
} else if (icon is Icon.Resource) {
val image = AnimatedImageVector.animatedVectorResource(id = icon.res)
val painter =
@@ -716,7 +769,7 @@
painter = painter,
contentDescription = icon.contentDescription?.load(),
colorFilter = ColorFilter.tint(color = color),
- modifier = iconModifier
+ modifier = iconModifier,
)
}
}
@@ -765,6 +818,8 @@
val TileHeight = 72.dp
val IconTileWithLabelHeight = 140.dp
+ @Composable fun longPressLabel() = stringResource(id = R.string.accessibility_long_click_tile)
+
/** An active tile without dual target uses the active color as background */
@Composable
fun activeTileColors(): TileColors =
@@ -850,7 +905,7 @@
} else {
InactiveCornerRadius
},
- label = label
+ label = label,
)
return RoundedCornerShape(animatedCornerRadius)
}
@@ -858,3 +913,14 @@
private const val CURRENT_TILES_GRID_TEST_TAG = "CurrentTilesGrid"
private const val AVAILABLE_TILES_GRID_TEST_TAG = "AvailableTilesGrid"
+
+/**
+ * A composable function that returns the [Resources]. It will be recomposed when [Configuration]
+ * gets updated.
+ */
+@Composable
+@ReadOnlyComposable
+private fun resources(): Resources {
+ LocalConfiguration.current
+ return LocalContext.current.resources
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
index 45051fe..aa42080 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/TileUiState.kt
@@ -16,8 +16,16 @@
package com.android.systemui.qs.panels.ui.viewmodel
+import android.content.res.Resources
+import android.service.quicksettings.Tile
+import android.text.TextUtils
+import android.widget.Switch
import androidx.compose.runtime.Immutable
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.state.ToggleableState
import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.qs.tileimpl.SubtitleArrayMapping
+import com.android.systemui.res.R
import java.util.function.Supplier
@Immutable
@@ -27,14 +35,78 @@
val state: Int,
val handlesSecondaryClick: Boolean,
val icon: Supplier<QSTile.Icon?>,
+ val accessibilityUiState: AccessibilityUiState,
)
-fun QSTile.State.toUiState(): TileUiState {
+data class AccessibilityUiState(
+ val contentDescription: String,
+ val stateDescription: String,
+ val accessibilityRole: Role,
+ val toggleableState: ToggleableState? = null,
+ val clickLabel: String? = null,
+)
+
+fun QSTile.State.toUiState(resources: Resources): TileUiState {
+ val accessibilityRole =
+ if (expandedAccessibilityClassName == Switch::class.java.name && !handlesSecondaryClick) {
+ Role.Switch
+ } else {
+ Role.Button
+ }
+ // State handling and description
+ val stateDescription = StringBuilder()
+ val stateText =
+ if (accessibilityRole == Role.Switch || state == Tile.STATE_UNAVAILABLE) {
+ getStateText(resources)
+ } else {
+ ""
+ }
+ val secondaryLabel = getSecondaryLabel(stateText)
+ if (!TextUtils.isEmpty(stateText)) {
+ stateDescription.append(stateText)
+ }
+ if (disabledByPolicy && state != Tile.STATE_UNAVAILABLE) {
+ stateDescription.append(", ")
+ stateDescription.append(getUnavailableText(spec, resources))
+ }
+ if (
+ !TextUtils.isEmpty(this.stateDescription) &&
+ !stateDescription.contains(this.stateDescription!!)
+ ) {
+ stateDescription.append(", ")
+ stateDescription.append(this.stateDescription)
+ }
+ val toggleableState =
+ if (accessibilityRole == Role.Switch || handlesSecondaryClick) {
+ ToggleableState(state == Tile.STATE_ACTIVE)
+ } else {
+ null
+ }
return TileUiState(
- label?.toString() ?: "",
- secondaryLabel?.toString() ?: "",
- state,
- handlesSecondaryClick,
- icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
+ label = label?.toString() ?: "",
+ secondaryLabel = secondaryLabel?.toString() ?: "",
+ state = if (disabledByPolicy) Tile.STATE_UNAVAILABLE else state,
+ handlesSecondaryClick = handlesSecondaryClick,
+ icon = icon?.let { Supplier { icon } } ?: iconSupplier ?: Supplier { null },
+ AccessibilityUiState(
+ contentDescription?.toString() ?: "",
+ stateDescription.toString(),
+ accessibilityRole,
+ toggleableState,
+ resources
+ .getString(R.string.accessibility_tile_disabled_by_policy_action_description)
+ .takeIf { disabledByPolicy },
+ ),
)
}
+
+private fun QSTile.State.getStateText(resources: Resources): CharSequence {
+ val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
+ val array = resources.getStringArray(arrayResId)
+ return array[state]
+}
+
+private fun getUnavailableText(spec: String?, resources: Resources): String {
+ val arrayResId = SubtitleArrayMapping.getSubtitleId(spec)
+ return resources.getStringArray(arrayResId)[Tile.STATE_UNAVAILABLE]
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 7ceb786..7bff827 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -32,7 +32,7 @@
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.Switch;
+import android.widget.Button;
import androidx.annotation.VisibleForTesting;
@@ -59,13 +59,13 @@
import com.android.systemui.res.R;
import com.android.systemui.statusbar.policy.BluetoothController;
-import kotlinx.coroutines.Job;
-
import java.util.List;
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import kotlinx.coroutines.Job;
+
/** Quick settings tile: Bluetooth **/
public class BluetoothTile extends QSTileImpl<BooleanState> {
@@ -147,6 +147,8 @@
}
}
+
+
@Override
public Intent getLongClickIntent() {
return new Intent(Settings.ACTION_BLUETOOTH_SETTINGS);
@@ -221,7 +223,7 @@
state.state = Tile.STATE_INACTIVE;
}
- state.expandedAccessibilityClassName = Switch.class.getName();
+ state.expandedAccessibilityClassName = Button.class.getName();
state.forceExpandIcon = mFeatureFlags.isEnabled(Flags.BLUETOOTH_QS_TILE_DIALOG);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 078698c..7606293 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -55,7 +55,7 @@
qsLogger: QSLogger,
private val keyguardStateController: KeyguardStateController,
private val dialogTransitionAnimator: DialogTransitionAnimator,
- private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>
+ private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>,
) :
QSTileImpl<QSTile.State?>(
host,
@@ -66,7 +66,7 @@
metricsLogger,
statusBarStateController,
activityStarter,
- qsLogger
+ qsLogger,
) {
private val icon = ResourceIcon.get(R.drawable.ic_qs_font_scaling)
@@ -86,7 +86,7 @@
expandable?.dialogTransitionController(
DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
- INTERACTION_JANK_TAG
+ INTERACTION_JANK_TAG,
)
)
controller?.let { dialogTransitionAnimator.show(dialog, controller) }
@@ -102,7 +102,7 @@
/* cancelAction= */ null,
/* dismissShade= */ true,
/* afterKeyguardGone= */ true,
- /* deferred= */ false
+ /* deferred= */ false,
)
}
}
@@ -110,6 +110,7 @@
override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
state?.label = mContext.getString(R.string.quick_settings_font_scaling_label)
state?.icon = icon
+ state?.contentDescription = state?.label
}
override fun getLongClickIntent(): Intent? {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
index b9f9b92..05f19ef 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/MultiUserUtilsModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.settings;
+import android.app.ActivityManager;
import android.app.IActivityManager;
import android.content.Context;
import android.hardware.display.DisplayManager;
@@ -66,7 +67,7 @@
@Background CoroutineDispatcher backgroundDispatcher,
@Background Handler handler
) {
- int startingUser = userManager.getBootUser().getIdentifier();
+ int startingUser = ActivityManager.getCurrentUser();
UserTrackerImpl tracker = new UserTrackerImpl(context, featureFlagsProvider, userManager,
iActivityManager, dumpManager, appScope, backgroundDispatcher, handler);
tracker.initialize(startingUser);
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index 1a997a7..e1631cc 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -16,11 +16,10 @@
package com.android.systemui.settings
-import com.android.systemui.util.annotations.WeaklyReferencedCallback
-
import android.content.Context
import android.content.pm.UserInfo
import android.os.UserHandle
+import com.android.systemui.util.annotations.WeaklyReferencedCallback
import java.util.concurrent.Executor
/**
@@ -31,19 +30,13 @@
*/
interface UserTracker : UserContentResolverProvider, UserContextProvider {
- /**
- * Current user's id.
- */
+ /** Current user's id. */
val userId: Int
- /**
- * [UserHandle] for current user
- */
+ /** [UserHandle] for current user */
val userHandle: UserHandle
- /**
- * [UserInfo] for current user
- */
+ /** [UserInfo] for current user */
val userInfo: UserInfo
/**
@@ -56,39 +49,33 @@
*/
val userProfiles: List<UserInfo>
- /**
- * Add a [Callback] to be notified of chances, on a particular [Executor]
- */
+ /** Is the system in the process of switching users? */
+ val isUserSwitching: Boolean
+
+ /** Add a [Callback] to be notified of chances, on a particular [Executor] */
fun addCallback(callback: Callback, executor: Executor)
- /**
- * Remove a [Callback] previously added.
- */
+ /** Remove a [Callback] previously added. */
fun removeCallback(callback: Callback)
- /**
- * Callback for notifying of changes.
- */
+ /** Callback for notifying of changes. */
@WeaklyReferencedCallback
interface Callback {
- /**
- * Notifies that the current user will be changed.
- */
+ /** Notifies that the current user will be changed. */
fun onBeforeUserSwitching(newUser: Int) {}
/**
- * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be
- * called automatically after the completion of this method.
+ * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be called
+ * automatically after the completion of this method.
*/
fun onUserChanging(newUser: Int, userContext: Context) {}
/**
- * Notifies that the current user is being changed.
- * Override this method to run things while the screen is frozen for the user switch.
- * Please use {@link #onUserChanged} if the task doesn't need to push the unfreezing of the
- * screen further. Please be aware that code executed in this callback will lengthen the
- * user switch duration. When overriding this method, resultCallback#run() MUST be called
- * once the execution is complete.
+ * Notifies that the current user is being changed. Override this method to run things while
+ * the screen is frozen for the user switch. Please use {@link #onUserChanged} if the task
+ * doesn't need to push the unfreezing of the screen further. Please be aware that code
+ * executed in this callback will lengthen the user switch duration. When overriding this
+ * method, resultCallback#run() MUST be called once the execution is complete.
*/
fun onUserChanging(newUser: Int, userContext: Context, resultCallback: Runnable) {
onUserChanging(newUser, userContext)
@@ -96,15 +83,13 @@
}
/**
- * Notifies that the current user has changed.
- * Override this method to run things after the screen is unfrozen for the user switch.
- * Please see {@link #onUserChanging} if you need to hide jank.
+ * Notifies that the current user has changed. Override this method to run things after the
+ * screen is unfrozen for the user switch. Please see {@link #onUserChanging} if you need to
+ * hide jank.
*/
fun onUserChanged(newUser: Int, userContext: Context) {}
- /**
- * Notifies that the current user's profiles have changed.
- */
+ /** Notifies that the current user's profiles have changed. */
fun onProfilesChanged(profiles: List<@JvmSuppressWildcards UserInfo>) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index 553d1f5..1863e12 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -106,6 +106,9 @@
override var userInfo: UserInfo by SynchronizedDelegate(UserInfo(context.userId, "", 0))
protected set
+ override var isUserSwitching = false
+ protected set
+
/**
* Returns a [List<UserInfo>] of all profiles associated with the current user.
*
@@ -197,6 +200,7 @@
}
override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
+ isUserSwitching = true
if (isBackgroundUserSwitchEnabled) {
userSwitchingJob?.cancel()
userSwitchingJob =
@@ -210,6 +214,7 @@
}
override fun onUserSwitchComplete(newUserId: Int) {
+ isUserSwitching = false
if (isBackgroundUserSwitchEnabled) {
afterUserSwitchingJob?.cancel()
afterUserSwitchingJob =
@@ -221,7 +226,7 @@
}
}
},
- TAG
+ TAG,
)
}
@@ -349,7 +354,7 @@
private data class DataItem(
val callback: WeakReference<UserTracker.Callback>,
- val executor: Executor
+ val executor: Executor,
) {
fun sameOrEmpty(other: UserTracker.Callback): Boolean {
return callback.get()?.equals(other) ?: true
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 52fde7e..4005e10 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -494,7 +494,7 @@
@Test
public void testIgnoresSimStateCallback_rebroadcast() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ Intent intent = defaultSimStateChangedIntent();
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(), intent);
mTestableLooper.processAllMessages();
@@ -515,7 +515,8 @@
@Test
public void testTelephonyCapable_SimState_Absent() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+
+ Intent intent = defaultSimStateChangedIntent();
intent.putExtra(Intent.EXTRA_SIM_STATE,
Intent.SIM_STATE_ABSENT);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
@@ -526,7 +527,7 @@
@Test
public void testTelephonyCapable_SimState_CardIOError() {
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ Intent intent = defaultSimStateChangedIntent();
intent.putExtra(Intent.EXTRA_SIM_STATE,
Intent.SIM_STATE_CARD_IO_ERROR);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext(),
@@ -593,7 +594,7 @@
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_OUT_OF_SERVICE);
state.fillInNotifierBundle(data);
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ Intent intent = defaultSimStateChangedIntent();
intent.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_NOT_READY);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -608,7 +609,7 @@
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_OUT_OF_SERVICE);
state.fillInNotifierBundle(data);
- Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ Intent intent = defaultSimStateChangedIntent();
intent.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_READY);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -649,7 +650,7 @@
ServiceState state = new ServiceState();
state.setState(ServiceState.STATE_IN_SERVICE);
state.fillInNotifierBundle(data);
- Intent intentSimState = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ Intent intentSimState = defaultSimStateChangedIntent();
intentSimState.putExtra(Intent.EXTRA_SIM_STATE
, Intent.SIM_STATE_LOADED);
mKeyguardUpdateMonitor.mBroadcastReceiver.onReceive(getContext()
@@ -2256,6 +2257,12 @@
Assert.assertFalse(mKeyguardUpdateMonitor.forceIsDismissibleIsKeepingDeviceUnlocked());
}
+ private Intent defaultSimStateChangedIntent() {
+ Intent intent = new Intent(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
+ intent.putExtra(SubscriptionManager.EXTRA_SLOT_INDEX, 0);
+ return intent;
+ }
+
private void verifyFingerprintAuthenticateNeverCalled() {
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), any());
verify(mFingerprintManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 597ffef..9e0d358 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -26,6 +26,7 @@
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR;
+import static com.android.systemui.Flags.FLAG_SIM_PIN_BOUNCER_RESET;
import static com.android.systemui.keyguard.KeyguardViewMediator.DELAYED_KEYGUARD_ACTION;
import static com.android.systemui.keyguard.KeyguardViewMediator.KEYGUARD_LOCK_AFTER_DELAY_DEFAULT;
import static com.android.systemui.keyguard.KeyguardViewMediator.REBOOT_MAINLINE_UPDATE;
@@ -62,6 +63,7 @@
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -588,6 +590,35 @@
@Test
@TestableLooper.RunWithLooper(setAsMainLooper = true)
+ @EnableFlags(FLAG_SIM_PIN_BOUNCER_RESET)
+ public void resetStateLocked_whenSimNotReadyAndWasLockedPrior() {
+ // When showing and provisioned
+ mViewMediator.onSystemReady();
+ when(mUpdateMonitor.isDeviceProvisioned()).thenReturn(true);
+ mViewMediator.setShowingLocked(true, "");
+
+ // and a SIM becomes locked and requires a PIN
+ mViewMediator.mUpdateCallback.onSimStateChanged(
+ 1 /* subId */,
+ 0 /* slotId */,
+ TelephonyManager.SIM_STATE_PIN_REQUIRED);
+ TestableLooper.get(this).processAllMessages();
+
+ reset(mStatusBarKeyguardViewManager);
+
+ // but then disabled by a NOT_READY
+ mViewMediator.mUpdateCallback.onSimStateChanged(
+ 1 /* subId */,
+ 0 /* slotId */,
+ TelephonyManager.SIM_STATE_NOT_READY);
+ TestableLooper.get(this).processAllMessages();
+
+ // A call to reset the keyguard and bouncer was invoked
+ verify(mStatusBarKeyguardViewManager).reset(true);
+ }
+
+ @Test
+ @TestableLooper.RunWithLooper(setAsMainLooper = true)
public void restoreBouncerWhenSimLockedAndKeyguardIsGoingAway_initiallyNotShowing() {
// When showing and provisioned
mViewMediator.onSystemReady();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
index 2e2ac3e..a0ecb80 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -231,7 +231,7 @@
"",
"",
UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ UserManager.USER_TYPE_PROFILE_MANAGED,
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
@@ -261,7 +261,7 @@
"",
"",
UserInfo.FLAG_MANAGED_PROFILE or UserInfo.FLAG_QUIET_MODE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ UserManager.USER_TYPE_PROFILE_MANAGED,
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
@@ -291,7 +291,7 @@
"",
"",
UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ UserManager.USER_TYPE_PROFILE_MANAGED,
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
@@ -423,7 +423,7 @@
"",
"",
UserInfo.FLAG_MANAGED_PROFILE,
- UserManager.USER_TYPE_PROFILE_MANAGED
+ UserManager.USER_TYPE_PROFILE_MANAGED,
)
infoProfile.profileGroupId = id
listOf(info, infoProfile)
@@ -469,6 +469,24 @@
assertThat(callback.calledOnProfilesChanged).isEqualTo(0)
}
+ @Test
+ fun testisUserSwitching() =
+ testScope.runTest {
+ tracker.initialize(0)
+ val newID = 5
+ val profileID = newID + 10
+
+ val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
+ verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+ assertThat(tracker.isUserSwitching).isFalse()
+
+ captor.value.onUserSwitching(newID, userSwitchingReply)
+ assertThat(tracker.isUserSwitching).isTrue()
+
+ captor.value.onUserSwitchComplete(newID)
+ assertThat(tracker.isUserSwitching).isFalse()
+ }
+
private class TestCallback : UserTracker.Callback {
var calledOnUserChanging = 0
var calledOnUserChanged = 0
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
index ace1157..52416ba 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractorKosmos.kt
@@ -41,5 +41,6 @@
trustRepository = trustRepository,
alternateBouncerInteractor = alternateBouncerInteractor,
powerInteractor = powerInteractor,
+ keyguardTransitionInteractor = keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
index f3d5b7d..cd76a74 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/settings/FakeUserTracker.kt
@@ -33,6 +33,7 @@
private var _userHandle: UserHandle = UserHandle.of(_userId),
private var _userInfo: UserInfo = mock(),
private var _userProfiles: List<UserInfo> = emptyList(),
+ private var _isUserSwitching: Boolean = false,
userContentResolverProvider: () -> ContentResolver = { MockContentResolver() },
userContext: Context = mock(),
private val onCreateCurrentUserContext: (Context) -> Context = { mock() },
@@ -51,6 +52,9 @@
override val userProfiles: List<UserInfo>
get() = _userProfiles
+ override val isUserSwitching: Boolean
+ get() = _isUserSwitching
+
// userContentResolver is lazy because Ravenwood doesn't support MockContentResolver()
// and we still want to allow people use this class for tests that don't use it.
override val userContentResolver: ContentResolver by lazy { userContentResolverProvider() }
@@ -86,11 +90,13 @@
}
fun onUserChanging(userId: Int = _userId) {
+ _isUserSwitching = true
val copy = callbacks.toList()
copy.forEach { it.onUserChanging(userId, userContext) {} }
}
fun onUserChanged(userId: Int = _userId) {
+ _isUserSwitching = false
val copy = callbacks.toList()
copy.forEach { it.onUserChanged(userId, userContext) }
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index acb74d3..c3d68cf 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -1643,16 +1643,14 @@
CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mSessionProcessor.deInitSession();
- if (Flags.surfaceLeakFix()) {
- if (mOutputImageCaptureSurfaceImpl.mSurface != null) {
- mOutputImageCaptureSurfaceImpl.mSurface.release();
- }
- if (mOutputPreviewSurfaceImpl.mSurface != null) {
- mOutputPreviewSurfaceImpl.mSurface.release();
- }
- if (mOutputPostviewSurfaceImpl.mSurface != null) {
- mOutputPostviewSurfaceImpl.mSurface.release();
- }
+ if (mOutputImageCaptureSurfaceImpl.mSurface != null) {
+ mOutputImageCaptureSurfaceImpl.mSurface.release();
+ }
+ if (mOutputPreviewSurfaceImpl.mSurface != null) {
+ mOutputPreviewSurfaceImpl.mSurface.release();
+ }
+ if (mOutputPostviewSurfaceImpl.mSurface != null) {
+ mOutputPostviewSurfaceImpl.mSurface.release();
}
}
diff --git a/ravenwood/TEST_MAPPING b/ravenwood/TEST_MAPPING
index 3583b96..d4d188d 100644
--- a/ravenwood/TEST_MAPPING
+++ b/ravenwood/TEST_MAPPING
@@ -159,11 +159,13 @@
{
"name": "RavenwoodServicesTest",
"host": true
- },
+ }
+ // AUTO-GENERATED-END
+ ],
+ "ravenwood-postsubmit": [
{
"name": "SystemUiRavenTests",
"host": true
}
- // AUTO-GENERATED-END
]
}
diff --git a/ravenwood/scripts/update-test-mapping.sh b/ravenwood/scripts/update-test-mapping.sh
index b6cf5b8..e478b50 100755
--- a/ravenwood/scripts/update-test-mapping.sh
+++ b/ravenwood/scripts/update-test-mapping.sh
@@ -20,6 +20,9 @@
set -e
+# Tests that shouldn't be in presubmit.
+EXEMPT='^(SystemUiRavenTests)$'
+
main() {
local script_name="${0##*/}"
local script_dir="${0%/*}"
@@ -30,7 +33,7 @@
local footer="$(sed -ne '/AUTO-GENERATED-END/,$p' "$test_mapping")"
echo "Getting all tests"
- local tests=( $("$script_dir/list-ravenwood-tests.sh") )
+ local tests=( $("$script_dir/list-ravenwood-tests.sh" | grep -vP "$EXEMPT") )
local num_tests="${#tests[@]}"
diff --git a/services/appfunctions/TEST_MAPPING b/services/appfunctions/TEST_MAPPING
index 91e82ec..91cfa06 100644
--- a/services/appfunctions/TEST_MAPPING
+++ b/services/appfunctions/TEST_MAPPING
@@ -1,4 +1,9 @@
{
+ "presubmit": [
+ {
+ "name": "FrameworksAppFunctionsTests"
+ }
+ ],
"postsubmit": [
{
"name": "FrameworksAppFunctionsTests"
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 269419f..2362b91 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -38,6 +38,7 @@
import com.android.server.appfunctions.RemoteServiceCaller.ServiceUsageCompleteListener;
import java.util.Objects;
+import java.util.concurrent.CompletionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -255,6 +256,10 @@
}
private ExecuteAppFunctionResponse mapExceptionToExecuteAppFunctionResponse(Throwable e) {
+ if(e instanceof CompletionException) {
+ e = e.getCause();
+ }
+
if (e instanceof AppSearchException) {
AppSearchException appSearchException = (AppSearchException) e;
return ExecuteAppFunctionResponse.newFailure(
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index ca8ae6e..72f840d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -21,6 +21,7 @@
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
import static com.android.hardware.input.Flags.touchpadVisualizer;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import android.Manifest;
import android.annotation.EnforcePermission;
@@ -2116,6 +2117,7 @@
mKeyboardBacklightController.dump(ipw);
mKeyboardLedController.dump(ipw);
mKeyboardGlyphManager.dump(ipw);
+ mKeyGestureController.dump(ipw);
}
private void dumpAssociations(IndentingPrintWriter pw) {
@@ -2457,13 +2459,18 @@
// Native callback.
@SuppressWarnings("unused")
- private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
- // TODO(b/358569822): Move shortcut trigger logic from PWM to KeyGestureController
- long value = mKeyGestureController.interceptKeyBeforeDispatching(focus, event, policyFlags);
- if (value != 0) { // If key is consumed (i.e. non-zero value)
- return value;
+ @VisibleForTesting
+ long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+ final long keyNotConsumed = 0;
+ long value = keyNotConsumed;
+ if (useKeyGestureEventHandler()) {
+ value = mKeyGestureController.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
- return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
+ if (value == keyNotConsumed) {
+ value = mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event,
+ policyFlags);
+ }
+ return value;
}
// Native callback.
@@ -2748,12 +2755,14 @@
private void enforceManageKeyGesturePermission() {
// TODO(b/361567988): Use @EnforcePermission to enforce permission once flag guarding the
// permission is rolled out
- String systemUIPackage = mContext.getString(R.string.config_systemUi);
- int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
- .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
- UserHandle.USER_SYSTEM));
- if (UserHandle.getCallingAppId() == systemUIAppId) {
- return;
+ if (mSystemReady) {
+ String systemUIPackage = mContext.getString(R.string.config_systemUi);
+ int systemUIAppId = UserHandle.getAppId(mPackageManagerInternal
+ .getPackageUid(systemUIPackage, PackageManager.MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM));
+ if (UserHandle.getCallingAppId() == systemUIAppId) {
+ return;
+ }
}
if (mContext.checkCallingOrSelfPermission(
Manifest.permission.MANAGE_KEY_GESTURES) == PackageManager.PERMISSION_GRANTED) {
diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java
index bfdb1c1..760566b 100644
--- a/services/core/java/com/android/server/input/KeyGestureController.java
+++ b/services/core/java/com/android/server/input/KeyGestureController.java
@@ -16,10 +16,15 @@
package com.android.server.input;
+import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
+
import android.annotation.BinderThread;
import android.annotation.MainThread;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.content.Context;
+import android.content.res.Resources;
+
import android.hardware.input.AidlKeyGestureEvent;
import android.hardware.input.IKeyGestureEventListener;
import android.hardware.input.IKeyGestureHandler;
@@ -31,6 +36,8 @@
import android.os.Message;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.IndentingPrintWriter;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
@@ -38,10 +45,14 @@
import android.view.InputDevice;
import android.view.KeyEvent;
+import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayDeque;
+import java.util.HashSet;
import java.util.Objects;
+import java.util.Set;
import java.util.TreeMap;
/**
@@ -56,12 +67,36 @@
// 'adb shell setprop log.tag.KeyGestureController DEBUG' (requires restart)
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ // Maximum key gesture events that are tracked and will be available in input dump.
+ private static final int MAX_TRACKED_EVENTS = 10;
+
private static final int MSG_NOTIFY_KEY_GESTURE_EVENT = 1;
+ // must match: config_settingsKeyBehavior in config.xml
+ private static final int SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0;
+ private static final int SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1;
+ private static final int SETTINGS_KEY_BEHAVIOR_NOTHING = 2;
+ private static final int LAST_SETTINGS_KEY_BEHAVIOR = SETTINGS_KEY_BEHAVIOR_NOTHING;
+
+ // Must match: config_searchKeyBehavior in config.xml
+ private static final int SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH = 0;
+ private static final int SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY = 1;
+ private static final int LAST_SEARCH_KEY_BEHAVIOR = SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY;
+
private final Context mContext;
private final Handler mHandler;
private final int mSystemPid;
+ // Pending actions
+ private boolean mPendingMetaAction;
+ private boolean mPendingCapsLockToggle;
+ private boolean mPendingHideRecentSwitcher;
+
+ // Key behaviors
+ private boolean mEnableBugReportKeyboardShortcut;
+ private int mSearchKeyBehavior;
+ private int mSettingsKeyBehavior;
+
// List of currently registered key gesture event listeners keyed by process pid
@GuardedBy("mKeyGestureEventListenerRecords")
private final SparseArray<KeyGestureEventListenerRecord>
@@ -73,6 +108,11 @@
@GuardedBy("mKeyGestureHandlerRecords")
private final TreeMap<Integer, KeyGestureHandlerRecord> mKeyGestureHandlerRecords;
+ private final ArrayDeque<KeyGestureEvent> mLastHandledEvents = new ArrayDeque<>();
+
+ /** Currently fully consumed key codes per device */
+ private final SparseArray<Set<Integer>> mConsumedKeysForDevice = new SparseArray<>();
+
KeyGestureController(Context context, Looper looper) {
mContext = context;
mHandler = new Handler(looper, this::handleMessage);
@@ -89,12 +129,449 @@
return Integer.compare(p1, p2);
}
});
+ initBehaviors();
}
- public int interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
+ private void initBehaviors() {
+ mEnableBugReportKeyboardShortcut = "1".equals(SystemProperties.get("ro.debuggable"));
+
+ Resources res = mContext.getResources();
+ mSearchKeyBehavior = res.getInteger(R.integer.config_searchKeyBehavior);
+ if (mSearchKeyBehavior < SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH
+ || mSearchKeyBehavior > LAST_SEARCH_KEY_BEHAVIOR) {
+ mSearchKeyBehavior = SEARCH_KEY_BEHAVIOR_DEFAULT_SEARCH;
+ }
+ mSettingsKeyBehavior = res.getInteger(R.integer.config_settingsKeyBehavior);
+ if (mSettingsKeyBehavior < SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY
+ || mSettingsKeyBehavior > LAST_SETTINGS_KEY_BEHAVIOR) {
+ mSettingsKeyBehavior = SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY;
+ }
+ }
+
+ public long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,
+ int policyFlags) {
// TODO(b/358569822): Handle shortcuts trigger logic here and pass it to appropriate
// KeyGestureHandler (PWM is one of the handlers)
- return 0;
+ final int keyCode = event.getKeyCode();
+ final int deviceId = event.getDeviceId();
+ final long keyConsumed = -1;
+ final long keyNotConsumed = 0;
+
+ Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
+ if (consumedKeys == null) {
+ consumedKeys = new HashSet<>();
+ mConsumedKeysForDevice.put(deviceId, consumedKeys);
+ }
+
+ if (interceptSystemKeysAndShortcuts(focusedToken, event)
+ && event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
+ consumedKeys.add(keyCode);
+ return keyConsumed;
+ }
+
+ boolean needToConsumeKey = consumedKeys.contains(keyCode);
+ if (event.getAction() == KeyEvent.ACTION_UP || event.isCanceled()) {
+ consumedKeys.remove(keyCode);
+ if (consumedKeys.isEmpty()) {
+ mConsumedKeysForDevice.remove(deviceId);
+ }
+ }
+
+ return needToConsumeKey ? keyConsumed : keyNotConsumed;
+ }
+
+ @SuppressLint("MissingPermission")
+ private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
+ final int keyCode = event.getKeyCode();
+ final int repeatCount = event.getRepeatCount();
+ final int metaState = event.getMetaState();
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ final boolean canceled = event.isCanceled();
+ final int displayId = event.getDisplayId();
+ final int deviceId = event.getDeviceId();
+ final boolean firstDown = down && repeatCount == 0;
+
+ // Cancel any pending meta actions if we see any other keys being pressed between the
+ // down of the meta key and its corresponding up.
+ if (mPendingMetaAction && !KeyEvent.isMetaKey(keyCode)) {
+ mPendingMetaAction = false;
+ }
+ // Any key that is not Alt or Meta cancels Caps Lock combo tracking.
+ if (mPendingCapsLockToggle && !KeyEvent.isMetaKey(keyCode) && !KeyEvent.isAltKey(keyCode)) {
+ mPendingCapsLockToggle = false;
+ }
+
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_A:
+ if (firstDown && event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_RECENT_APPS:
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_APP_SWITCH:
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+ KeyGestureEvent.ACTION_GESTURE_START, displayId,
+ focusedToken, /* flags = */0);
+ } else if (!down) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, canceled ? KeyGestureEvent.FLAG_CANCELLED : 0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_H:
+ case KeyEvent.KEYCODE_ENTER:
+ if (firstDown && event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_I:
+ if (firstDown && event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_L:
+ if (firstDown && event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_N:
+ if (firstDown && event.isMetaPressed()) {
+ if (event.isCtrlPressed()) {
+ // TODO(b/358569822): Move open notes handling in System UI instead of PWM
+ } else {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_S:
+ if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_DEL:
+ if (newBugreportKeyboardShortcut()) {
+ if (firstDown && mEnableBugReportKeyboardShortcut && event.isMetaPressed()
+ && event.isCtrlPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ // fall through
+ case KeyEvent.KEYCODE_ESCAPE:
+ if (firstDown && event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_UP:
+ if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ if (firstDown && event.isMetaPressed()) {
+ if (event.isCtrlPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ } else if (event.isAltPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ } else {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ if (firstDown && event.isMetaPressed()) {
+ if (event.isCtrlPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ } else if (event.isAltPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_SLASH:
+ if (firstDown && event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ break;
+ case KeyEvent.KEYCODE_BRIGHTNESS_UP:
+ case KeyEvent.KEYCODE_BRIGHTNESS_DOWN:
+ if (down) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP
+ ? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP
+ : KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
+ if (down) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP:
+ if (down) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE:
+ // TODO: Add logic
+ if (!down) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_ALL_APPS:
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_NOTIFICATION:
+ if (!down) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_SEARCH:
+ if (firstDown && mSearchKeyBehavior == SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+
+ }
+ break;
+ case KeyEvent.KEYCODE_SETTINGS:
+ if (firstDown) {
+ if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY) {
+ handleKeyGesture(deviceId,
+ new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ } else if (mSettingsKeyBehavior == SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL) {
+ handleKeyGesture(deviceId,
+ new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ return true;
+ case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{keyCode},
+ event.isShiftPressed() ? KeyEvent.META_SHIFT_ON : 0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_CAPS_LOCK:
+ // Just logging/notifying purposes
+ // Caps lock is already handled in inputflinger native
+ if (!down) {
+ AidlKeyGestureEvent eventToNotify = createKeyGestureEvent(deviceId,
+ new int[]{keyCode}, metaState,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, /* flags = */0);
+ Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT,
+ eventToNotify);
+ mHandler.sendMessage(msg);
+ }
+ break;
+ case KeyEvent.KEYCODE_SCREENSHOT:
+ if (firstDown) {
+ handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ return true;
+ case KeyEvent.KEYCODE_META_LEFT:
+ case KeyEvent.KEYCODE_META_RIGHT:
+ if (down) {
+ if (event.isAltPressed()) {
+ mPendingCapsLockToggle = true;
+ mPendingMetaAction = false;
+ } else {
+ mPendingCapsLockToggle = false;
+ mPendingMetaAction = true;
+ }
+ } else {
+ // Toggle Caps Lock on META-ALT.
+ if (mPendingCapsLockToggle) {
+ mPendingCapsLockToggle = false;
+ handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+
+ } else if (mPendingMetaAction) {
+ mPendingMetaAction = false;
+ if (!canceled) {
+ handleKeyGesture(deviceId, new int[]{keyCode},
+ /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ }
+ return true;
+ case KeyEvent.KEYCODE_TAB:
+ if (firstDown) {
+ if (event.isMetaPressed()) {
+ return handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_META_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ } else if (!mPendingHideRecentSwitcher) {
+ final int shiftlessModifiers =
+ event.getModifiers() & ~KeyEvent.META_SHIFT_MASK;
+ if (KeyEvent.metaStateHasModifiers(
+ shiftlessModifiers, KeyEvent.META_ALT_ON)) {
+ mPendingHideRecentSwitcher = true;
+ return handleKeyGesture(deviceId, new int[]{keyCode},
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+ KeyGestureEvent.ACTION_GESTURE_START, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ if (down) {
+ if (event.isMetaPressed()) {
+ mPendingCapsLockToggle = true;
+ mPendingMetaAction = false;
+ } else {
+ mPendingCapsLockToggle = false;
+ }
+ } else {
+ if (mPendingHideRecentSwitcher) {
+ mPendingHideRecentSwitcher = false;
+ return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_TAB},
+ KeyEvent.META_ALT_ON,
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+
+ // Toggle Caps Lock on META-ALT.
+ if (mPendingCapsLockToggle) {
+ mPendingCapsLockToggle = false;
+ return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0,
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId,
+ focusedToken, /* flags = */0);
+ }
+ }
+ break;
+ case KeyEvent.KEYCODE_ASSIST:
+ Slog.wtf(TAG, "KEYCODE_ASSIST should be handled in interceptKeyBeforeQueueing");
+ return true;
+ case KeyEvent.KEYCODE_VOICE_ASSIST:
+ Slog.wtf(TAG, "KEYCODE_VOICE_ASSIST should be handled in"
+ + " interceptKeyBeforeQueueing");
+ return true;
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
+ Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
+ + " interceptKeyBeforeQueueing");
+ return true;
+ }
+ return false;
}
@VisibleForTesting
@@ -147,6 +624,10 @@
KeyGestureEvent.keyGestureTypeToLogEvent(event.gestureType));
}
notifyAllListeners(event);
+ while (mLastHandledEvents.size() >= MAX_TRACKED_EVENTS) {
+ mLastHandledEvents.removeFirst();
+ }
+ mLastHandledEvents.addLast(new KeyGestureEvent(event));
}
@MainThread
@@ -347,4 +828,45 @@
event.flags = flags;
return event;
}
+
+ public void dump(IndentingPrintWriter ipw) {
+ ipw.println("KeyGestureController:");
+ ipw.increaseIndent();
+ ipw.println("mSystemPid = " + mSystemPid);
+ ipw.println("mPendingMetaAction = " + mPendingMetaAction);
+ ipw.println("mPendingCapsLockToggle = " + mPendingCapsLockToggle);
+ ipw.println("mPendingHideRecentSwitcher = " + mPendingHideRecentSwitcher);
+ ipw.println("mSearchKeyBehavior = " + mSearchKeyBehavior);
+ ipw.println("mSettingsKeyBehavior = " + mSettingsKeyBehavior);
+ ipw.print("mKeyGestureEventListenerRecords = {");
+ synchronized (mKeyGestureEventListenerRecords) {
+ int size = mKeyGestureEventListenerRecords.size();
+ for (int i = 0; i < size; i++) {
+ ipw.print(mKeyGestureEventListenerRecords.keyAt(i));
+ if (i < size - 1) {
+ ipw.print(", ");
+ }
+ }
+ }
+ ipw.println("}");
+ ipw.print("mKeyGestureHandlerRecords = {");
+ synchronized (mKeyGestureHandlerRecords) {
+ int i = mKeyGestureHandlerRecords.size() - 1;
+ for (int processId : mKeyGestureHandlerRecords.keySet()) {
+ ipw.print(processId);
+ if (i > 0) {
+ ipw.print(", ");
+ }
+ i--;
+ }
+ }
+ ipw.println("}");
+ ipw.decreaseIndent();
+ ipw.println("Last handled KeyGestureEvents: ");
+ ipw.increaseIndent();
+ for (KeyGestureEvent ev : mLastHandledEvents) {
+ ipw.println(ev);
+ }
+ ipw.decreaseIndent();
+ }
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index ed9dcfa..98091b1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -83,6 +83,7 @@
import static com.android.hardware.input.Flags.emojiAndScreenshotKeycodesAvailable;
import static com.android.hardware.input.Flags.modifierShortcutDump;
+import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
@@ -809,7 +810,7 @@
event.recycle();
break;
case MSG_HANDLE_ALL_APPS:
- launchAllAppsAction((KeyEvent) msg.obj);
+ launchAllAppsAction();
break;
case MSG_RINGER_TOGGLE_CHORD:
handleRingerChordGesture();
@@ -820,7 +821,7 @@
case MSG_SWITCH_KEYBOARD_LAYOUT:
SwitchKeyboardLayoutMessageObject object =
(SwitchKeyboardLayoutMessageObject) msg.obj;
- handleSwitchKeyboardLayout(object.keyEvent, object.direction,
+ handleSwitchKeyboardLayout(object.displayId, object.direction,
object.focusedToken);
break;
case MSG_SET_DEFERRED_KEY_ACTIONS_EXECUTABLE:
@@ -937,7 +938,7 @@
}
}
- private record SwitchKeyboardLayoutMessageObject(KeyEvent keyEvent, IBinder focusedToken,
+ private record SwitchKeyboardLayoutMessageObject(int displayId, IBinder focusedToken,
int direction) {
}
@@ -1813,9 +1814,7 @@
Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
}
- private void handleShortPressOnHome(KeyEvent event) {
- notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
-
+ private void handleShortPressOnHome(int displayId) {
// Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
final HdmiControl hdmiControl = getHdmiControl();
if (hdmiControl != null) {
@@ -1831,7 +1830,7 @@
}
// Go home!
- launchHomeFromHotKey(event.getDisplayId());
+ launchHomeFromHotKey(displayId);
}
/**
@@ -1878,11 +1877,9 @@
}
}
- private void launchAllAppsAction(KeyEvent event) {
+ private void launchAllAppsAction() {
if (mHasFeatureLeanback || mHasFeatureWatch) {
// TV and watch support the all apps intent
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
Intent intent = new Intent(Intent.ACTION_ALL_APPS);
if (mHasFeatureLeanback) {
Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
@@ -1896,8 +1893,6 @@
}
startActivityAsUser(intent, UserHandle.CURRENT);
} else {
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
if (accessibilityManager != null) {
accessibilityManager.performSystemAction(
@@ -1949,7 +1944,9 @@
@Override
public void run() {
if (mPendingHomeKeyEvent != null) {
- handleShortPressOnHome(mPendingHomeKeyEvent);
+ notifyKeyGestureCompleted(mPendingHomeKeyEvent,
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
+ handleShortPressOnHome(mPendingHomeKeyEvent.getDisplayId());
mPendingHomeKeyEvent = null;
}
}
@@ -2002,7 +1999,10 @@
}
// Post to main thread to avoid blocking input pipeline.
- mHandler.post(() -> handleShortPressOnHome(event));
+ mHandler.post(() -> {
+ notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_HOME);
+ handleShortPressOnHome(event.getDisplayId());
+ });
return true;
}
@@ -2080,7 +2080,8 @@
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, "Home - Long Press");
switch (mLongPressOnHomeBehavior) {
case LONG_PRESS_HOME_ALL_APPS:
- launchAllAppsAction(event);
+ notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+ launchAllAppsAction();
break;
case LONG_PRESS_HOME_ASSIST:
notifyKeyGestureCompleted(event,
@@ -2430,6 +2431,7 @@
mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
initKeyCombinationRules();
initSingleKeyGestureRules(injector.getLooper());
+ initKeyGestures();
mButtonOverridePermissionChecker = injector.getButtonOverridePermissionChecker();
mSideFpsEventHandler = new SideFpsEventHandler(mContext, mHandler, mPowerManager);
}
@@ -3348,18 +3350,18 @@
}
}
- Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
- if (consumedKeys == null) {
- consumedKeys = new HashSet<>();
- mConsumedKeysForDevice.put(deviceId, consumedKeys);
- }
-
// TODO(b/358569822) Remove below once we have nicer API for listening to shortcuts
if ((event.isMetaPressed() || KeyEvent.isMetaKey(keyCode))
&& shouldInterceptShortcuts(focusedToken)) {
return keyNotConsumed;
}
+ Set<Integer> consumedKeys = mConsumedKeysForDevice.get(deviceId);
+ if (consumedKeys == null) {
+ consumedKeys = new HashSet<>();
+ mConsumedKeysForDevice.put(deviceId, consumedKeys);
+ }
+
if (interceptSystemKeysAndShortcuts(focusedToken, event)
&& event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) {
consumedKeys.add(keyCode);
@@ -3388,6 +3390,10 @@
// conflicting events to application, make sure to consume the event on
// ACTION_DOWN even if you want to do something on ACTION_UP. This is essential
// to maintain event parity and to not have incomplete key gestures.
+ //
+ // NOTE: Please try not to add new Shortcut combinations here and instead use KeyGestureEvents.
+ // Add shortcut trigger logic in {@code KeyGestureController} and add handling logic in
+ // {@link handleKeyGesture()}
@SuppressLint("MissingPermission")
private boolean interceptSystemKeysAndShortcuts(IBinder focusedToken, KeyEvent event) {
final boolean keyguardOn = keyguardOn();
@@ -3516,6 +3522,7 @@
injectBackGesture(event.getDownTime());
return true;
}
+ break;
case KeyEvent.KEYCODE_DPAD_UP:
if (firstDown && event.isMetaPressed() && event.isCtrlPressed()) {
StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
@@ -3544,11 +3551,11 @@
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
true /* leftOrTop */);
notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT);
} else if (event.isAltPressed()) {
setSplitscreenFocus(true /* leftOrTop */);
notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT);
} else {
notifyKeyGestureCompleted(event,
KeyGestureEvent.KEY_GESTURE_TYPE_BACK);
@@ -3563,12 +3570,12 @@
moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyEvent(event),
false /* leftOrTop */);
notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION);
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT);
return true;
} else if (event.isAltPressed()) {
setSplitscreenFocus(false /* leftOrTop */);
notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS);
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT);
return true;
}
}
@@ -3593,30 +3600,7 @@
if (down) {
int direction = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_UP ? 1 : -1;
- int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
-
- float minLinearBrightness = mPowerManager.getBrightnessConstraint(
- PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
- float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
- PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
- float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);
-
- float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
- float adjustedGammaBrightness =
- gammaBrightness + 1f / BRIGHTNESS_STEPS * direction;
- adjustedGammaBrightness = MathUtils.constrain(adjustedGammaBrightness, 0f,
- 1f);
- float adjustedLinearBrightness = BrightnessUtils.convertGammaToLinear(
- adjustedGammaBrightness);
- adjustedLinearBrightness = MathUtils.constrain(adjustedLinearBrightness,
- minLinearBrightness, maxLinearBrightness);
- mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
-
- Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
- intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
- | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
- startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ changeDisplayBrightnessValue(displayId, direction);
int gestureType = keyCode == KeyEvent.KEYCODE_BRIGHTNESS_DOWN
? KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN
@@ -3689,10 +3673,11 @@
case KeyEvent.KEYCODE_ALL_APPS:
if (firstDown) {
mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
-
Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS, new KeyEvent(event));
msg.setAsynchronous(true);
msg.sendToTarget();
+
+ notifyKeyGestureCompleted(event, KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
@@ -3720,7 +3705,7 @@
case KeyEvent.KEYCODE_LANGUAGE_SWITCH:
if (firstDown) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- sendSwitchKeyboardLayout(event, focusedToken, direction);
+ sendSwitchKeyboardLayout(displayId, focusedToken, direction);
notifyKeyGestureCompleted(event,
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH);
return true;
@@ -3745,7 +3730,9 @@
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
} else if (mPendingMetaAction) {
if (!canceled) {
- launchAllAppsAction(event);
+ launchAllAppsAction();
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
}
mPendingMetaAction = false;
}
@@ -3829,6 +3816,254 @@
return (metaState & KeyEvent.META_META_ON) != 0;
}
+ @SuppressLint("MissingPermission")
+ private void initKeyGestures() {
+ if (!useKeyGestureEventHandler()) {
+ return;
+ }
+ mInputManager.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() {
+ @Override
+ public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event,
+ @Nullable IBinder focusedToken) {
+ return PhoneWindowManager.this.handleKeyGestureEvent(event, focusedToken);
+ }
+
+ @Override
+ public boolean isKeyGestureSupported(int gestureType) {
+ switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ return true;
+ default:
+ return false;
+ }
+ }
+ });
+ }
+
+ @VisibleForTesting
+ boolean handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) {
+ boolean start = event.getAction() == KeyGestureEvent.ACTION_GESTURE_START;
+ boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ && !event.isCancelled();
+ int deviceId = event.getDeviceId();
+ int gestureType = event.getKeyGestureType();
+ int displayId = event.getDisplayId();
+ int modifierState = event.getModifierState();
+ boolean keyguardOn = keyguardOn();
+ switch (gestureType) {
+ case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS:
+ if (complete) {
+ showRecentApps(false);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH:
+ if (!keyguardOn) {
+ if (start) {
+ preloadRecentApps();
+ } else if (complete) {
+ toggleRecentApps();
+ }
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT:
+ if (complete) {
+ launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
+ deviceId, SystemClock.uptimeMillis(),
+ AssistUtils.INVOCATION_TYPE_UNKNOWN);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_HOME:
+ if (complete) {
+ // Post to main thread to avoid blocking input pipeline.
+ mHandler.post(() -> handleShortPressOnHome(displayId));
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS:
+ if (complete) {
+ showSystemSettings();
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN:
+ if (complete) {
+ lockNow(null /* options */);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL:
+ if (complete) {
+ toggleNotificationPanel();
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT:
+ if (complete) {
+ interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT:
+ if (complete && mEnableBugReportKeyboardShortcut) {
+ try {
+ mActivityManagerService.requestInteractiveBugReport();
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Error taking bugreport", e);
+ }
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_BACK:
+ if (complete) {
+ injectBackGesture(SystemClock.uptimeMillis());
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION:
+ if (complete) {
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.moveFocusedTaskToFullscreen(
+ getTargetDisplayIdForKeyGestureEvent(event));
+ }
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE:
+ if (complete) {
+ StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
+ if (statusbar != null) {
+ statusbar.moveFocusedTaskToDesktop(
+ getTargetDisplayIdForKeyGestureEvent(event));
+ }
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT:
+ if (complete) {
+ moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
+ true /* leftOrTop */);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT:
+ if (complete) {
+ setSplitscreenFocus(true /* leftOrTop */);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT:
+ if (complete) {
+ moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event),
+ false /* leftOrTop */);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT:
+ if (complete) {
+ setSplitscreenFocus(false /* leftOrTop */);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER:
+ if (complete) {
+ toggleKeyboardShortcutsMenu(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN:
+ if (complete) {
+ int direction =
+ gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP ? 1 : -1;
+ changeDisplayBrightnessValue(displayId, direction);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER:
+ if (start) {
+ showRecentApps(true);
+ } else {
+ hideRecentApps(true, false);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS:
+ case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS:
+ if (complete) {
+ launchAllAppsAction();
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH:
+ if (complete) {
+ launchTargetSearchActivity();
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH:
+ if (complete) {
+ int direction = (modifierState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
+ sendSwitchKeyboardLayout(displayId, focusedToken, direction);
+ }
+ return true;
+ // TODO (b/358569822): Move these input specific gesture handling to IMS since we
+ // are calling into InputManager through internal API anyways
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP:
+ if (complete) {
+ mInputManagerInternal.incrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN:
+ if (complete) {
+ mInputManagerInternal.decrementKeyboardBacklight(deviceId);
+ }
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE:
+ return true;
+ case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK:
+ if (complete) {
+ mInputManagerInternal.toggleCapsLock(deviceId);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private void changeDisplayBrightnessValue(int displayId, int direction) {
+ int screenDisplayId = displayId < 0 ? DEFAULT_DISPLAY : displayId;
+
+ float minLinearBrightness = mPowerManager.getBrightnessConstraint(
+ PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
+ float maxLinearBrightness = mPowerManager.getBrightnessConstraint(
+ PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
+ float linearBrightness = mDisplayManager.getBrightness(screenDisplayId);
+
+ float gammaBrightness = BrightnessUtils.convertLinearToGamma(linearBrightness);
+ float adjustedGammaBrightness = gammaBrightness + 1f / BRIGHTNESS_STEPS * direction;
+ adjustedGammaBrightness = MathUtils.constrain(adjustedGammaBrightness, 0f, 1f);
+ float adjustedLinearBrightness = BrightnessUtils.convertGammaToLinear(
+ adjustedGammaBrightness);
+ adjustedLinearBrightness = MathUtils.constrain(adjustedLinearBrightness,
+ minLinearBrightness, maxLinearBrightness);
+ mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
+
+ Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION
+ | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
+ }
+
private boolean shouldInterceptShortcuts(IBinder focusedToken) {
KeyInterceptionInfo info =
mWindowManagerInternal.getKeyInterceptionInfoFromToken(focusedToken);
@@ -4081,7 +4316,7 @@
if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK,
KeyEvent.META_CTRL_ON)) {
int direction = (metaState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1;
- sendSwitchKeyboardLayout(event, focusedToken, direction);
+ sendSwitchKeyboardLayout(event.getDisplayId(), focusedToken, direction);
return true;
}
}
@@ -4139,19 +4374,18 @@
}
}
- private void sendSwitchKeyboardLayout(@NonNull KeyEvent event,
- @Nullable IBinder focusedToken, int direction) {
+ private void sendSwitchKeyboardLayout(int displayId, @Nullable IBinder focusedToken,
+ int direction) {
SwitchKeyboardLayoutMessageObject object =
- new SwitchKeyboardLayoutMessageObject(event, focusedToken, direction);
+ new SwitchKeyboardLayoutMessageObject(displayId, focusedToken, direction);
mHandler.obtainMessage(MSG_SWITCH_KEYBOARD_LAYOUT, object).sendToTarget();
}
- private void handleSwitchKeyboardLayout(@NonNull KeyEvent event, int direction,
- IBinder focusedToken) {
+ private void handleSwitchKeyboardLayout(int displayId, int direction, IBinder focusedToken) {
IBinder targetWindowToken =
mWindowManagerInternal.getTargetWindowTokenFromInputToken(focusedToken);
- InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction,
- event.getDisplayId(), targetWindowToken);
+ InputMethodManagerInternal.get().onSwitchKeyboardLayoutShortcut(direction, displayId,
+ targetWindowToken);
}
private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,
@@ -7000,16 +7234,22 @@
}
private int getTargetDisplayIdForKeyEvent(KeyEvent event) {
- int displayId = event.getDisplayId();
-
- if (displayId == INVALID_DISPLAY) {
- displayId = mTopFocusedDisplayId;
+ if (event.getDisplayId() != INVALID_DISPLAY) {
+ return event.getDisplayId();
}
-
- if (displayId == INVALID_DISPLAY) {
- return DEFAULT_DISPLAY;
- } else {
- return displayId;
+ if (mTopFocusedDisplayId != INVALID_DISPLAY) {
+ return mTopFocusedDisplayId;
}
+ return DEFAULT_DISPLAY;
+ }
+
+ private int getTargetDisplayIdForKeyGestureEvent(KeyGestureEvent event) {
+ if (event.getDisplayId() != INVALID_DISPLAY) {
+ return event.getDisplayId();
+ }
+ if (mTopFocusedDisplayId != INVALID_DISPLAY) {
+ return mTopFocusedDisplayId;
+ }
+ return DEFAULT_DISPLAY;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 185dd0c..ccc9b17 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2641,7 +2641,7 @@
return true;
}
// Only do transfer after transaction has done when starting window exist.
- if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommitCount > 0) {
+ if (mStartingData != null && mStartingData.mWaitForSyncTransactionCommit) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
return true;
}
@@ -2804,11 +2804,9 @@
@Override
void waitForSyncTransactionCommit(ArraySet<WindowContainer> wcAwaitingCommit) {
- // Only add once per transition.
- final boolean added = wcAwaitingCommit.contains(this);
super.waitForSyncTransactionCommit(wcAwaitingCommit);
- if (!added && mStartingData != null) {
- mStartingData.mWaitForSyncTransactionCommitCount++;
+ if (mStartingData != null) {
+ mStartingData.mWaitForSyncTransactionCommit = true;
}
}
@@ -2819,7 +2817,7 @@
return;
}
final StartingData lastData = mStartingData;
- lastData.mWaitForSyncTransactionCommitCount--;
+ lastData.mWaitForSyncTransactionCommit = false;
if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) {
removeStartingWindowAnimation(lastData.mPrepareRemoveAnimation);
} else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) {
@@ -2849,7 +2847,7 @@
final boolean animate;
final boolean hasImeSurface;
if (mStartingData != null) {
- if (mStartingData.mWaitForSyncTransactionCommitCount > 0
+ if (mStartingData.mWaitForSyncTransactionCommit
|| mSyncState != SYNC_STATE_NONE) {
mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
mStartingData.mPrepareRemoveAnimation = prepareAnimation;
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 22c7e8c..24fb207 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -69,7 +69,7 @@
* Note this isn't equal to transition playing, the period should be
* Sync finishNow -> Start transaction apply.
*/
- int mWaitForSyncTransactionCommitCount;
+ boolean mWaitForSyncTransactionCommit;
/**
* For Shell transition.
@@ -112,7 +112,7 @@
public String toString() {
return getClass().getSimpleName() + "{"
+ Integer.toHexString(System.identityHashCode(this))
- + " mWaitForSyncTransactionCommitCount=" + mWaitForSyncTransactionCommitCount
+ + " waitForSyncTransactionCommit=" + mWaitForSyncTransactionCommit
+ " removeAfterTransaction= " + mRemoveAfterTransaction
+ "}";
}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 4eba36f..655a6fb 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2875,19 +2875,12 @@
return out;
}
- // Get the animation theme from the top-most application window
- // when Flags.customAnimationsBehindTranslucent() is false.
final AnimationOptions animOptionsForActivityTransition =
calculateAnimationOptionsForActivityTransition(type, sortedTargets);
-
if (!Flags.moveAnimationOptionsToChange() && animOptionsForActivityTransition != null) {
out.setAnimationOptions(animOptionsForActivityTransition);
}
- // Store the animation options of the topmost non-translucent change
- // (Used when Flags.customAnimationsBehindTranslucent() is true)
- AnimationOptions activityAboveAnimationOptions = null;
-
final ArraySet<WindowContainer> occludedAtEndContainers = new ArraySet<>();
// Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
final int count = sortedTargets.size();
@@ -3006,26 +2999,9 @@
change.setBackgroundColor(ColorUtils.setAlphaComponent(backgroundColor, 255));
}
- // Calculate the animation options for this change
+ AnimationOptions animOptions = null;
if (Flags.moveAnimationOptionsToChange()) {
- AnimationOptions animOptions = null;
- if (Flags.customAnimationsBehindTranslucent() && activityRecord != null) {
- if (activityAboveAnimationOptions != null) {
- // Inherit the options from one of the changes on top of this
- animOptions = activityAboveAnimationOptions;
- } else {
- // Create the options based on this change's custom animations and layout
- // parameters
- animOptions = getOptions(activityRecord /* customAnimActivity */,
- activityRecord /* animLpActivity */);
- if (!change.hasFlags(FLAG_TRANSLUCENT)) {
- // If this change is not translucent, its options are going to be
- // inherited by the changes below
- activityAboveAnimationOptions = animOptions;
- }
- }
- } else if (activityRecord != null && animOptionsForActivityTransition != null) {
- // Use the same options from the top activity for all the activities
+ if (activityRecord != null && animOptionsForActivityTransition != null) {
animOptions = animOptionsForActivityTransition;
} else if (Flags.activityEmbeddingOverlayPresentationFlag()
&& isEmbeddedTaskFragment) {
@@ -3074,42 +3050,28 @@
@Nullable
private static AnimationOptions calculateAnimationOptionsForActivityTransition(
@TransitionType int type, @NonNull ArrayList<ChangeInfo> sortedTargets) {
+ TransitionInfo.AnimationOptions animOptions = null;
+
+ // Check if the top-most app is an activity (ie. activity->activity). If so, make sure
+ // to honor its custom transition options.
WindowContainer<?> topApp = null;
for (int i = 0; i < sortedTargets.size(); i++) {
- if (!isWallpaper(sortedTargets.get(i).mContainer)) {
- topApp = sortedTargets.get(i).mContainer;
- break;
- }
+ if (isWallpaper(sortedTargets.get(i).mContainer)) continue;
+ topApp = sortedTargets.get(i).mContainer;
+ break;
}
- ActivityRecord animLpActivity = findAnimLayoutParamsActivityRecord(type, sortedTargets);
- return getOptions(topApp.asActivityRecord() /* customAnimActivity */,
- animLpActivity /* animLpActivity */);
- }
-
- /**
- * Updates and returns animOptions with the layout parameters of animLpActivity
- * @param customAnimActivity the activity that drives the custom animation options
- * @param animLpActivity the activity that drives the animation options with its layout
- * parameters
- * @return the options extracted from the provided activities
- */
- @Nullable
- private static AnimationOptions getOptions(@Nullable ActivityRecord customAnimActivity,
- @Nullable ActivityRecord animLpActivity) {
- AnimationOptions animOptions = null;
- // Custom
- if (customAnimActivity != null) {
- animOptions = addCustomActivityTransition(customAnimActivity, true /* open */,
- animOptions);
- animOptions = addCustomActivityTransition(customAnimActivity, false /* open */,
+ if (topApp.asActivityRecord() != null) {
+ final ActivityRecord topActivity = topApp.asActivityRecord();
+ animOptions = addCustomActivityTransition(topActivity, true/* open */,
+ null /* animOptions */);
+ animOptions = addCustomActivityTransition(topActivity, false/* open */,
animOptions);
}
-
- // Layout parameters
+ final ActivityRecord animLpActivity =
+ findAnimLayoutParamsActivityRecord(type, sortedTargets);
final WindowState mainWindow = animLpActivity != null
? animLpActivity.findMainWindow() : null;
- final WindowManager.LayoutParams animLp = mainWindow != null ? mainWindow.mAttrs : null;
-
+ WindowManager.LayoutParams animLp = mainWindow != null ? mainWindow.mAttrs : null;
if (animLp != null && animLp.type != TYPE_APPLICATION_STARTING
&& animLp.windowAnimations != 0) {
// Don't send animation options if no windowAnimations have been set or if the we
@@ -3249,9 +3211,10 @@
return ancestor;
}
- @Nullable
- private static ActivityRecord findAnimLayoutParamsActivityRecord(
- @TransitionType int transit, @NonNull List<ChangeInfo> sortedTargets) {
+ private static ActivityRecord findAnimLayoutParamsActivityRecord(int type,
+ ArrayList<ChangeInfo> sortedTargets) {
+ // Find the layout params of the top-most application window that is part of the
+ // transition, which is what will control the animation theme.
final ArraySet<Integer> activityTypes = new ArraySet<>();
final int targetCount = sortedTargets.size();
for (int i = 0; i < targetCount; ++i) {
@@ -3271,7 +3234,12 @@
// activity through the layout parameter animation style.
return null;
}
+ return findAnimLayoutParamsActivityRecord(sortedTargets, type, activityTypes);
+ }
+ private static ActivityRecord findAnimLayoutParamsActivityRecord(
+ List<ChangeInfo> sortedTargets,
+ @TransitionType int transit, ArraySet<Integer> activityTypes) {
// Remote animations always win, but fullscreen windows override non-fullscreen windows.
ActivityRecord result = lookForTopWindowWithFilter(sortedTargets,
w -> w.getRemoteAnimationDefinition() != null
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index b6e45fc8..6314b85 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10625,8 +10625,16 @@
final DevicePolicyData policyData = getUserData(userId);
if (transitionCheckNeeded) {
// Optional state transition check for non-ADB case.
- checkUserProvisioningStateTransition(policyData.mUserProvisioningState,
- newState);
+ try {
+ checkUserProvisioningStateTransition(
+ policyData.mUserProvisioningState,
+ newState);
+
+ } catch (IllegalStateException e) {
+ Slogf.e(LOG_TAG,
+ "Exception caught while changing provisioning state", e);
+ throw e;
+ }
}
policyData.mUserProvisioningState = newState;
saveSettingsLocked(userId);
@@ -10637,6 +10645,10 @@
}
private void checkUserProvisioningStateTransition(int currentState, int newState) {
+ if (Flags.userProvisioningSameState()) {
+ Preconditions.checkState(newState != currentState, "New state cannot"
+ + " be the same as the current state: [" + newState + "]");
+ }
// Valid transitions for normal use-cases.
switch (currentState) {
case DevicePolicyManager.STATE_USER_UNMANAGED:
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
index 1db9e8d..6393e11 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/Android.bp
@@ -1,7 +1,7 @@
aconfig_declarations {
name: "device_state_flags",
package: "com.android.server.policy.feature.flags",
- container: "system_ext",
+ container: "system",
srcs: [
"device_state_flags.aconfig",
],
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
index f827b55..21e33dd 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/feature/device_state_flags.aconfig
@@ -1,5 +1,5 @@
package: "com.android.server.policy.feature.flags"
-container: "system_ext"
+container: "system"
flag {
name: "enable_dual_display_blocking"
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 8f3adba..3d978e4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -16,6 +16,8 @@
package com.android.server.policy;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.policy.PhoneWindowManager.DOUBLE_TAP_HOME_RECENT_SYSTEM_UI;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ALL_APPS;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_HOME_ASSIST;
@@ -23,21 +25,21 @@
import static com.android.server.policy.PhoneWindowManager.SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL;
import android.hardware.input.KeyGestureEvent;
+import android.os.RemoteException;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.KeyEvent;
import androidx.test.filters.MediumTest;
import com.android.internal.annotations.Keep;
+import junit.framework.Assert;
+
import junitparams.JUnitParamsRunner;
import junitparams.Parameters;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,10 +48,6 @@
@RunWith(JUnitParamsRunner.class)
public class KeyGestureEventTests extends ShortcutKeyTestBase {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
private static final int META_KEY = KeyEvent.KEYCODE_META_LEFT;
private static final int META_ON = MODIFIER.get(KeyEvent.KEYCODE_META_LEFT);
private static final int ALT_KEY = KeyEvent.KEYCODE_ALT_LEFT;
@@ -149,9 +147,9 @@
{"VOLUME_MUTE key -> Mute Volume", new int[]{KeyEvent.KEYCODE_VOLUME_MUTE},
KeyGestureEvent.KEY_GESTURE_TYPE_VOLUME_MUTE,
KeyEvent.KEYCODE_VOLUME_MUTE, 0},
- {"ALL_APPS key -> Open App Drawer in Accessibility mode",
+ {"ALL_APPS key -> Open App Drawer",
new int[]{KeyEvent.KEYCODE_ALL_APPS},
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
KeyEvent.KEYCODE_ALL_APPS, 0},
{"SEARCH key -> Launch Search Activity", new int[]{KeyEvent.KEYCODE_SEARCH},
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH,
@@ -160,8 +158,8 @@
new int[]{KeyEvent.KEYCODE_LANGUAGE_SWITCH},
KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
KeyEvent.KEYCODE_LANGUAGE_SWITCH, 0},
- {"META key -> Open App Drawer in Accessibility mode", new int[]{META_KEY},
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, META_KEY,
+ {"META key -> Open App Drawer", new int[]{META_KEY},
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, META_KEY,
META_ON},
{"Meta + Alt -> Toggle CapsLock", new int[]{META_KEY, ALT_KEY},
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, ALT_KEY,
@@ -182,12 +180,12 @@
META_ON | CTRL_ON},
{"Meta + Ctrl + DPAD_LEFT -> Split screen navigation",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_LEFT},
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
KeyEvent.KEYCODE_DPAD_LEFT,
META_ON | CTRL_ON},
{"Meta + Ctrl + DPAD_RIGHT -> Split screen navigation",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DPAD_RIGHT},
- KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION,
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
KeyEvent.KEYCODE_DPAD_RIGHT,
META_ON | CTRL_ON},
{"Meta + L -> Lock Homescreen", new int[]{META_KEY, KeyEvent.KEYCODE_L},
@@ -320,18 +318,18 @@
new int[]{META_KEY, KeyEvent.KEYCODE_H}, LONG_PRESS_HOME_ASSIST,
KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, KeyEvent.KEYCODE_H,
META_ON},
- {"Long press HOME key -> Open App Drawer in Accessibility mode",
+ {"Long press HOME key -> Open App Drawer",
new int[]{KeyEvent.KEYCODE_HOME}, LONG_PRESS_HOME_ALL_APPS,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
KeyEvent.KEYCODE_HOME, 0},
- {"Long press META + ENTER -> Open App Drawer in Accessibility mode",
+ {"Long press META + ENTER -> Open App Drawer",
new int[]{META_KEY, KeyEvent.KEYCODE_ENTER}, LONG_PRESS_HOME_ALL_APPS,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
KeyEvent.KEYCODE_ENTER, META_ON},
- {"Long press META + H -> Open App Drawer in Accessibility mode",
+ {"Long press META + H -> Open App Drawer",
new int[]{META_KEY, KeyEvent.KEYCODE_H},
LONG_PRESS_HOME_ALL_APPS,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
KeyEvent.KEYCODE_H, META_ON}};
}
@@ -428,7 +426,7 @@
}
@Test
- @RequiresFlagsEnabled(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
+ @EnableFlags(com.android.server.flags.Flags.FLAG_NEW_BUGREPORT_KEYBOARD_SHORTCUT)
public void testBugreportShortcutPress() {
testShortcutInternal("Meta + Ctrl + Del -> Trigger bug report",
new int[]{META_KEY, CTRL_KEY, KeyEvent.KEYCODE_DEL},
@@ -444,4 +442,161 @@
new int[]{expectedKey}, expectedModifierState, expectedKeyGestureType,
"Failed while executing " + testName);
}
+
+ @Test
+ public void testKeyGestureRecentApps() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS));
+ mPhoneWindowManager.assertShowRecentApps();
+ }
+
+ @Test
+ public void testKeyGestureAppSwitch() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH));
+ mPhoneWindowManager.assertToggleRecentApps();
+ }
+
+ @Test
+ public void testKeyGestureLaunchAssistant() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT));
+ mPhoneWindowManager.assertSearchManagerLaunchAssist();
+ }
+
+ @Test
+ public void testKeyGestureGoHome() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME));
+ mPhoneWindowManager.assertGoToHomescreen();
+ }
+
+ @Test
+ public void testKeyGestureLaunchSystemSettings() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS));
+ mPhoneWindowManager.assertLaunchSystemSettings();
+ }
+
+ @Test
+ public void testKeyGestureLock() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN));
+ mPhoneWindowManager.assertLockedAfterAppTransitionFinished();
+ }
+
+ @Test
+ public void testKeyGestureToggleNotificationPanel() throws RemoteException {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL));
+ mPhoneWindowManager.assertTogglePanel();
+ }
+
+ @Test
+ public void testKeyGestureScreenshot() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT));
+ mPhoneWindowManager.assertTakeScreenshotCalled();
+ }
+
+ @Test
+ public void testKeyGestureTriggerBugReport() throws RemoteException {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT));
+ mPhoneWindowManager.assertTakeBugreport(true);
+ }
+
+ @Test
+ public void testKeyGestureBack() {
+ Assert.assertTrue(sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK));
+ mPhoneWindowManager.assertBackEventInjected();
+ }
+
+ @Test
+ public void testKeyGestureMultiWindowNavigation() {
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION));
+ mPhoneWindowManager.assertMoveFocusedTaskToFullscreen();
+ }
+
+ @Test
+ public void testKeyGestureDesktopMode() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE));
+ mPhoneWindowManager.assertMoveFocusedTaskToDesktop();
+ }
+
+ @Test
+ public void testKeyGestureSplitscreenNavigation() {
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT));
+ mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(true);
+
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT));
+ mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(false);
+ }
+
+ @Test
+ public void testKeyGestureSplitscreenFocus() {
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT));
+ mPhoneWindowManager.assertSetSplitscreenFocus(true);
+
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT));
+ mPhoneWindowManager.assertSetSplitscreenFocus(false);
+ }
+
+ @Test
+ public void testKeyGestureShortcutHelper() {
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER));
+ mPhoneWindowManager.assertToggleShortcutsMenu();
+ }
+
+ @Test
+ public void testKeyGestureBrightnessChange() {
+ float[] currentBrightness = new float[]{0.1f, 0.05f, 0.0f};
+ float[] newBrightness = new float[]{0.065738f, 0.0275134f, 0.0f};
+
+ for (int i = 0; i < currentBrightness.length; i++) {
+ mPhoneWindowManager.prepareBrightnessDecrease(currentBrightness[i]);
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN));
+ mPhoneWindowManager.verifyNewBrightness(newBrightness[i]);
+ }
+ }
+
+ @Test
+ public void testKeyGestureRecentAppSwitcher() {
+ Assert.assertTrue(sendKeyGestureEventStart(
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER));
+ mPhoneWindowManager.assertShowRecentApps();
+
+ Assert.assertTrue(sendKeyGestureEventComplete(
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER));
+ mPhoneWindowManager.assertHideRecentApps();
+ }
+
+ @Test
+ public void testKeyGestureLanguageSwitch() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH));
+ mPhoneWindowManager.assertSwitchKeyboardLayout(1, DEFAULT_DISPLAY);
+
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ KeyEvent.META_SHIFT_ON));
+ mPhoneWindowManager.assertSwitchKeyboardLayout(-1, DEFAULT_DISPLAY);
+ }
+
+ @Test
+ public void testKeyGestureLaunchSearch() {
+ Assert.assertTrue(
+ sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH));
+ mPhoneWindowManager.assertLaunchSearch();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
index 536dcfb..af3dc1d 100644
--- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java
@@ -46,6 +46,7 @@
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
+import android.hardware.input.InputManager;
import android.os.PowerManager;
import android.platform.test.flag.junit.SetFlagsRule;
@@ -95,6 +96,9 @@
mStatusBarManagerInternal = mock(StatusBarManagerInternal.class);
LocalServices.addService(StatusBarManagerInternal.class, mStatusBarManagerInternal);
mPhoneWindowManager.mKeyguardDelegate = mock(KeyguardServiceDelegate.class);
+ final InputManager im = mock(InputManager.class);
+ doNothing().when(im).registerKeyGestureEventHandler(any());
+ doReturn(im).when(mContext).getSystemService(eq(Context.INPUT_SERVICE));
}
@After
diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
index 37e4fd6..50b7db4 100644
--- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
+++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java
@@ -56,6 +56,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
+import android.hardware.input.KeyGestureEvent;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import android.view.InputDevice;
@@ -228,6 +229,31 @@
sendKeyCombination(new int[]{keyCode}, 0 /*durationMillis*/, longPress, DEFAULT_DISPLAY);
}
+ boolean sendKeyGestureEventStart(int gestureType) {
+ return mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
+ KeyGestureEvent.ACTION_GESTURE_START).build());
+ }
+
+ boolean sendKeyGestureEventComplete(int gestureType) {
+ return mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+ }
+
+ boolean sendKeyGestureEventComplete(int gestureType, int modifierState) {
+ return mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder().setModifierState(modifierState).setKeyGestureType(
+ gestureType).setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+ }
+
+ boolean sendKeyGestureEventComplete(int keycode, int modifierState, int gestureType) {
+ return mPhoneWindowManager.sendKeyGestureEvent(
+ new KeyGestureEvent.Builder().setKeycodes(new int[]{keycode}).setModifierState(
+ modifierState).setKeyGestureType(gestureType).setAction(
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE).build());
+ }
+
/**
* Since we use SettingsProviderRule to mock the ContentResolver in these
* tests, the settings observer registered by PhoneWindowManager will not
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index 79c7ac1..0b55e2b 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -71,6 +71,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
+import android.hardware.input.KeyGestureEvent;
import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -83,9 +84,11 @@
import android.os.Vibrator;
import android.os.VibratorInfo;
import android.os.test.TestLooper;
+import android.provider.Settings;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
import android.view.Display;
+import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.autofill.AutofillManagerInternal;
@@ -118,6 +121,7 @@
import org.mockito.MockitoAnnotations;
import org.mockito.quality.Strictness;
+import java.util.List;
import java.util.function.Supplier;
class TestPhoneWindowManager {
@@ -298,6 +302,8 @@
doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class));
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class));
+ doNothing().when(mInputManager).registerKeyGestureEventHandler(any());
+ doNothing().when(mInputManager).unregisterKeyGestureEventHandler(any());
doReturn(mPackageManager).when(mContext).getPackageManager();
doReturn(mSensorPrivacyManager).when(mContext).getSystemService(
eq(SensorPrivacyManager.class));
@@ -417,6 +423,10 @@
mPhoneWindowManager.dispatchUnhandledKey(mInputToken, event, FLAG_INTERACTIVE);
}
+ boolean sendKeyGestureEvent(KeyGestureEvent event) {
+ return mPhoneWindowManager.handleKeyGestureEvent(event, mInputToken);
+ }
+
/**
* Provide access to the SettingsObserver so that tests can manually trigger Settings changes.
*/
@@ -584,6 +594,16 @@
doReturn(true).when(mInputManager).injectInputEvent(any(KeyEvent.class), anyInt());
}
+ void assertBackEventInjected() {
+ ArgumentCaptor<InputEvent> intentCaptor = ArgumentCaptor.forClass(InputEvent.class);
+ verify(mInputManager, times(2)).injectInputEvent(intentCaptor.capture(), anyInt());
+ List<InputEvent> inputEvents = intentCaptor.getAllValues();
+ Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(0)).getKeyCode());
+ Assert.assertEquals(KeyEvent.KEYCODE_BACK, ((KeyEvent) inputEvents.get(1)).getKeyCode());
+ // Reset verifier for next call.
+ Mockito.clearInvocations(mContext);
+ }
+
void overrideSearchKeyBehavior(int behavior) {
mPhoneWindowManager.mSearchKeyBehavior = behavior;
}
@@ -685,6 +705,24 @@
verify(mSearchManager).launchAssist(any());
}
+ void assertLaunchSystemSettings() {
+ mTestLooper.dispatchAll();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivityAsUser(intentCaptor.capture(), any(), any());
+ Assert.assertEquals(Settings.ACTION_SETTINGS, intentCaptor.getValue().getAction());
+ // Reset verifier for next call.
+ Mockito.clearInvocations(mContext);
+ }
+
+ void assertLaunchSearch() {
+ mTestLooper.dispatchAll();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivityAsUser(intentCaptor.capture(), any(), any());
+ Assert.assertEquals(Intent.ACTION_WEB_SEARCH, intentCaptor.getValue().getAction());
+ // Reset verifier for next call.
+ Mockito.clearInvocations(mContext);
+ }
+
void assertLaunchCategory(String category) {
mTestLooper.dispatchAll();
ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
@@ -725,6 +763,36 @@
verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
}
+ void assertHideRecentApps() {
+ mTestLooper.dispatchAll();
+ verify(mStatusBarManagerInternal).hideRecentApps(anyBoolean(), anyBoolean());
+ }
+
+ void assertToggleRecentApps() {
+ mTestLooper.dispatchAll();
+ verify(mStatusBarManagerInternal).toggleRecentApps();
+ }
+
+ void assertMoveFocusedTaskToDesktop() {
+ mTestLooper.dispatchAll();
+ verify(mStatusBarManagerInternal).moveFocusedTaskToDesktop(anyInt());
+ }
+
+ void assertMoveFocusedTaskToFullscreen() {
+ mTestLooper.dispatchAll();
+ verify(mStatusBarManagerInternal).moveFocusedTaskToFullscreen(anyInt());
+ }
+
+ void assertMoveFocusedTaskToStageSplit(boolean leftOrTop) {
+ mTestLooper.dispatchAll();
+ verify(mStatusBarManagerInternal).moveFocusedTaskToStageSplit(anyInt(), eq(leftOrTop));
+ }
+
+ void assertSetSplitscreenFocus(boolean leftOrTop) {
+ mTestLooper.dispatchAll();
+ verify(mStatusBarManagerInternal).setSplitscreenFocus(eq(leftOrTop));
+ }
+
void assertStatusBarStartAssist() {
mTestLooper.dispatchAll();
verify(mStatusBarManagerInternal).startAssist(any());
diff --git a/tests/FlickerTests/test-apps/flickerapp/Android.bp b/tests/FlickerTests/test-apps/flickerapp/Android.bp
index e3b23b9..a186679 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -19,6 +19,7 @@
// to get the below license kinds:
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["frameworks_base_license"],
+ default_team: "trendy_team_windowing_tools",
}
android_test {
diff --git a/tests/Input/Android.bp b/tests/Input/Android.bp
index f3e040a..6742cbe 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -39,6 +39,7 @@
"flag-junit",
"frameworks-base-testutils",
"hamcrest-library",
+ "junit-params",
"kotlin-test",
"mockito-target-extended-minus-junit4",
"platform-test-annotations",
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index e126797..28550cd 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -25,19 +25,27 @@
import android.hardware.input.InputManager
import android.hardware.input.InputManagerGlobal
import android.hardware.input.KeyGestureEvent
+import android.hardware.input.KeyGestureEvent.KeyGestureType
import android.os.IBinder
import android.os.Process
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
+import android.view.KeyCharacterMap
import android.view.KeyEvent
import androidx.test.core.app.ApplicationProvider
+import com.android.internal.annotations.Keep
import com.android.internal.util.FrameworkStatsLog
import com.android.modules.utils.testing.ExtendedMockitoRule
+import junitparams.JUnitParamsRunner
+import junitparams.Parameters
+import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Rule
import org.junit.Test
+import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
@@ -48,6 +56,7 @@
* atest InputTests:KeyGestureControllerTests
*/
@Presubmit
+@RunWith(JUnitParamsRunner::class)
class KeyGestureControllerTests {
companion object {
@@ -59,6 +68,16 @@
.setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)
.setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
.build()
+ val MODIFIER = mapOf(
+ KeyEvent.KEYCODE_CTRL_LEFT to (KeyEvent.META_CTRL_LEFT_ON or KeyEvent.META_CTRL_ON),
+ KeyEvent.KEYCODE_CTRL_RIGHT to (KeyEvent.META_CTRL_RIGHT_ON or KeyEvent.META_CTRL_ON),
+ KeyEvent.KEYCODE_ALT_LEFT to (KeyEvent.META_ALT_LEFT_ON or KeyEvent.META_ALT_ON),
+ KeyEvent.KEYCODE_ALT_RIGHT to (KeyEvent.META_ALT_RIGHT_ON or KeyEvent.META_ALT_ON),
+ KeyEvent.KEYCODE_SHIFT_LEFT to (KeyEvent.META_SHIFT_LEFT_ON or KeyEvent.META_SHIFT_ON),
+ KeyEvent.KEYCODE_SHIFT_RIGHT to (KeyEvent.META_SHIFT_RIGHT_ON or KeyEvent.META_SHIFT_ON),
+ KeyEvent.KEYCODE_META_LEFT to (KeyEvent.META_META_LEFT_ON or KeyEvent.META_META_ON),
+ KeyEvent.KEYCODE_META_RIGHT to (KeyEvent.META_META_RIGHT_ON or KeyEvent.META_META_ON),
+ )
}
@JvmField
@@ -75,6 +94,7 @@
private lateinit var inputManagerGlobalSession: InputManagerGlobal.TestSession
private lateinit var testLooper: TestLooper
private var events = mutableListOf<KeyGestureEvent>()
+ private var handleEvents = mutableListOf<KeyGestureEvent>()
@Before
fun setup() {
@@ -184,6 +204,440 @@
)
}
+ class TestData(
+ val name: String,
+ val keys: IntArray,
+ val expectedKeyGestureType: Int,
+ val expectedKeys: IntArray,
+ val expectedModifierState: Int,
+ val expectedActions: IntArray,
+ ) {
+ override fun toString(): String = name
+ }
+
+ @Keep
+ private fun keyGestureEventHandlerTestArguments(): Array<TestData> {
+ return arrayOf(
+ TestData(
+ "META + A -> Launch Assistant",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_A),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT,
+ intArrayOf(KeyEvent.KEYCODE_A),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "RECENT_APPS -> Show Overview",
+ intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ intArrayOf(KeyEvent.KEYCODE_RECENT_APPS),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "APP_SWITCH -> App Switch",
+ intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
+ KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH,
+ intArrayOf(KeyEvent.KEYCODE_APP_SWITCH),
+ 0,
+ intArrayOf(
+ KeyGestureEvent.ACTION_GESTURE_START,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ )
+ ),
+ TestData(
+ "META + H -> Go Home",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_H),
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ intArrayOf(KeyEvent.KEYCODE_H),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ENTER -> Go Home",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ENTER),
+ KeyGestureEvent.KEY_GESTURE_TYPE_HOME,
+ intArrayOf(KeyEvent.KEYCODE_ENTER),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + I -> Launch System Settings",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_I),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS,
+ intArrayOf(KeyEvent.KEYCODE_I),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + L -> Lock",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_L),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN,
+ intArrayOf(KeyEvent.KEYCODE_L),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + N -> Toggle Notification",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_N),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ intArrayOf(KeyEvent.KEYCODE_N),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + S -> Take Screenshot",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_S
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ intArrayOf(KeyEvent.KEYCODE_S),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + DEL -> Back",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_DEL),
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+ intArrayOf(KeyEvent.KEYCODE_DEL),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ESC -> Back",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ESCAPE),
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+ intArrayOf(KeyEvent.KEYCODE_ESCAPE),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + DPAD_LEFT -> Back",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_DPAD_LEFT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_BACK,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + DPAD_UP -> Multi Window Navigation",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_DPAD_UP
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_UP),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + DPAD_DOWN -> Desktop Mode",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_DPAD_DOWN
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_DOWN),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + DPAD_LEFT -> Splitscreen Navigation Left",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_DPAD_LEFT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + DPAD_RIGHT -> Splitscreen Navigation Right",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_CTRL_LEFT,
+ KeyEvent.KEYCODE_DPAD_RIGHT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT + DPAD_LEFT -> Change Splitscreen Focus Left",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_LEFT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_LEFT),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + CTRL + DPAD_RIGHT -> Change Splitscreen Focus Right",
+ intArrayOf(
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_ALT_LEFT,
+ KeyEvent.KEYCODE_DPAD_RIGHT
+ ),
+ KeyGestureEvent.KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT,
+ intArrayOf(KeyEvent.KEYCODE_DPAD_RIGHT),
+ KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "BRIGHTNESS_UP -> Brightness Up",
+ intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
+ KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP,
+ intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_UP),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "BRIGHTNESS_DOWN -> Brightness Down",
+ intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN),
+ KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN,
+ intArrayOf(KeyEvent.KEYCODE_BRIGHTNESS_DOWN),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "KEYBOARD_BACKLIGHT_UP -> Keyboard Backlight Up",
+ intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP),
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP,
+ intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "KEYBOARD_BACKLIGHT_DOWN -> Keyboard Backlight Down",
+ intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN),
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN,
+ intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "KEYBOARD_BACKLIGHT_TOGGLE -> Keyboard Backlight Toggle",
+ intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE),
+ KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE,
+ intArrayOf(KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALL_APPS -> Open App Drawer",
+ intArrayOf(KeyEvent.KEYCODE_ALL_APPS),
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS,
+ intArrayOf(KeyEvent.KEYCODE_ALL_APPS),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "NOTIFICATION -> Toggle Notification Panel",
+ intArrayOf(KeyEvent.KEYCODE_NOTIFICATION),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL,
+ intArrayOf(KeyEvent.KEYCODE_NOTIFICATION),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "LANGUAGE_SWITCH -> Switch Language Forward",
+ intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "SHIFT + LANGUAGE_SWITCH -> Switch Language Backward",
+ intArrayOf(KeyEvent.KEYCODE_SHIFT_LEFT, KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+ KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH,
+ intArrayOf(KeyEvent.KEYCODE_LANGUAGE_SWITCH),
+ KeyEvent.META_SHIFT_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "SCREENSHOT -> Take Screenshot",
+ intArrayOf(KeyEvent.KEYCODE_SCREENSHOT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT,
+ intArrayOf(KeyEvent.KEYCODE_SCREENSHOT),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META -> Open Apps Drawer",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS,
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + ALT -> Toggle Caps Lock",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + META -> Toggle Caps Lock",
+ intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_META_LEFT),
+ KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK,
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT),
+ 0,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "META + TAB -> Open Overview",
+ intArrayOf(KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_TAB),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS,
+ intArrayOf(KeyEvent.KEYCODE_TAB),
+ KeyEvent.META_META_ON,
+ intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+ ),
+ TestData(
+ "ALT + TAB -> Toggle Recent Apps Switcher",
+ intArrayOf(KeyEvent.KEYCODE_ALT_LEFT, KeyEvent.KEYCODE_TAB),
+ KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER,
+ intArrayOf(KeyEvent.KEYCODE_TAB),
+ KeyEvent.META_ALT_ON,
+ intArrayOf(
+ KeyGestureEvent.ACTION_GESTURE_START,
+ KeyGestureEvent.ACTION_GESTURE_COMPLETE
+ )
+ ),
+ )
+ }
+
+ @Test
+ @Parameters(method = "keyGestureEventHandlerTestArguments")
+ fun testKeyGestures(test: TestData) {
+ val handler = KeyGestureHandler { event, _ ->
+ handleEvents.add(KeyGestureEvent(event))
+ true
+ }
+ keyGestureController.registerKeyGestureHandler(handler, 0)
+ handleEvents.clear()
+
+ sendKeys(test.keys, /* assertAllConsumed = */ false)
+
+ assertEquals(
+ "Test: $test doesn't produce correct number of key gesture events",
+ test.expectedActions.size,
+ handleEvents.size
+ )
+ for (i in handleEvents.indices) {
+ val event = handleEvents[i]
+ assertArrayEquals(
+ "Test: $test doesn't produce correct key gesture keycodes",
+ test.expectedKeys,
+ event.keycodes
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture modifier state",
+ test.expectedModifierState,
+ event.modifierState
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture type",
+ test.expectedKeyGestureType,
+ event.keyGestureType
+ )
+ assertEquals(
+ "Test: $test doesn't produce correct key gesture action",
+ test.expectedActions[i],
+ event.action
+ )
+ }
+
+ keyGestureController.unregisterKeyGestureHandler(handler, 0)
+ }
+
+ @Test
+ fun testKeycodesFullyConsumed_irrespectiveOfHandlers() {
+ val testKeys = intArrayOf(
+ KeyEvent.KEYCODE_RECENT_APPS,
+ KeyEvent.KEYCODE_APP_SWITCH,
+ KeyEvent.KEYCODE_BRIGHTNESS_UP,
+ KeyEvent.KEYCODE_BRIGHTNESS_DOWN,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_UP,
+ KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE,
+ KeyEvent.KEYCODE_ALL_APPS,
+ KeyEvent.KEYCODE_NOTIFICATION,
+ KeyEvent.KEYCODE_SETTINGS,
+ KeyEvent.KEYCODE_LANGUAGE_SWITCH,
+ KeyEvent.KEYCODE_SCREENSHOT,
+ KeyEvent.KEYCODE_META_LEFT,
+ KeyEvent.KEYCODE_META_RIGHT,
+ KeyEvent.KEYCODE_ASSIST,
+ KeyEvent.KEYCODE_VOICE_ASSIST,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL,
+ )
+
+ val handler = KeyGestureHandler { _, _ -> false }
+ keyGestureController.registerKeyGestureHandler(handler, 0)
+
+ for (key in testKeys) {
+ sendKeys(intArrayOf(key), /* assertAllConsumed = */ true)
+ }
+ }
+
+ private fun sendKeys(testKeys: IntArray, assertAllConsumed: Boolean) {
+ var metaState = 0
+ for (key in testKeys) {
+ val downEvent = KeyEvent(
+ /* downTime = */0, /* eventTime = */ 0, KeyEvent.ACTION_DOWN, key,
+ 0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+ 0 /*flags*/, InputDevice.SOURCE_KEYBOARD
+ )
+ val consumed =
+ keyGestureController.interceptKeyBeforeDispatching(null, downEvent, 0) == -1L
+ if (assertAllConsumed) {
+ assertTrue(
+ "interceptKeyBeforeDispatching should consume all events $downEvent",
+ consumed
+ )
+ }
+ metaState = metaState or MODIFIER.getOrDefault(key, 0)
+
+ downEvent.recycle()
+ testLooper.dispatchAll()
+ }
+
+ for (key in testKeys.reversed()) {
+ val upEvent = KeyEvent(
+ /* downTime = */0, /* eventTime = */ 0, KeyEvent.ACTION_UP, key,
+ 0 /*repeat*/, metaState, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /*scancode*/,
+ 0 /*flags*/, InputDevice.SOURCE_KEYBOARD
+ )
+ val consumed =
+ keyGestureController.interceptKeyBeforeDispatching(null, upEvent, 0) == -1L
+ if (assertAllConsumed) {
+ assertTrue(
+ "interceptKeyBeforeDispatching should consume all events $upEvent",
+ consumed
+ )
+ }
+
+ upEvent.recycle()
+ testLooper.dispatchAll()
+ }
+ }
+
inner class KeyGestureEventListener : IKeyGestureEventListener.Stub() {
override fun onKeyGestureEvent(event: AidlKeyGestureEvent) {
events.add(KeyGestureEvent(event))