Merge "Group session MediaItems together in OutputSwitcher" into main
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 5e0428b..05d6e88 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -29,8 +29,5 @@
ktlint_hook = ${REPO_ROOT}/prebuilts/ktlint/ktlint.py --no-verify-format -f ${PREUPLOAD_FILES}
-# This flag check hook runs only for "packages/SystemUI" subdirectory. If you want to include this check for other subdirectories, please modify flag_check.py.
-flag_hook = ${REPO_ROOT}/frameworks/base/packages/SystemUI/flag_check.py --msg=${PREUPLOAD_COMMIT_MESSAGE} --files=${PREUPLOAD_FILES} --project=${REPO_PROJECT}
-
[Tool Paths]
ktfmt = ${REPO_ROOT}/external/ktfmt/ktfmt.sh
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index 1e299cd..f16f2ca 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -50,6 +50,7 @@
"junit-params",
"core-tests-support",
"guava",
+ "perfetto_trace_java_protos",
],
libs: ["android.test.base.stubs.system"],
diff --git a/apct-tests/perftests/core/src/android/os/TracePerfTest.java b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
index 0d64c39..bf7c96a 100644
--- a/apct-tests/perftests/core/src/android/os/TracePerfTest.java
+++ b/apct-tests/perftests/core/src/android/os/TracePerfTest.java
@@ -17,6 +17,8 @@
package android.os;
+import static android.os.PerfettoTrace.Category;
+
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.perftests.utils.ShellHelper;
@@ -31,19 +33,35 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import perfetto.protos.DataSourceConfigOuterClass.DataSourceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.BufferConfig;
+import perfetto.protos.TraceConfigOuterClass.TraceConfig.DataSource;
+import perfetto.protos.TrackEventConfigOuterClass.TrackEventConfig;
+
@RunWith(AndroidJUnit4.class)
public class TracePerfTest {
@Rule
public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private static final String FOO = "foo";
+ private static final Category FOO_CATEGORY = new Category(FOO);
+ private static PerfettoTrace.Session sPerfettoSession;
+
@BeforeClass
public static void startTracing() {
ShellHelper.runShellCommandRaw("atrace -c --async_start -a *");
+ PerfettoTrace.register(false /* isBackendInProcess */);
+ FOO_CATEGORY.register();
+ sPerfettoSession = new PerfettoTrace.Session(false /* isBackendInProcess */,
+ getTraceConfig(FOO).toByteArray());
}
@AfterClass
public static void endTracing() {
ShellHelper.runShellCommandRaw("atrace --async_stop");
+ FOO_CATEGORY.unregister();
+ sPerfettoSession.close();
}
@Before
@@ -84,4 +102,61 @@
Trace.setCounter("testCounter", 123);
}
}
+
+ @Test
+ public void testInstant() {
+ Trace.instant(Trace.TRACE_TAG_APP, "testInstantA");
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Trace.instant(Trace.TRACE_TAG_APP, "testInstantA");
+ }
+ }
+
+ @Test
+ public void testInstantPerfetto() {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP").emit();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP").emit();
+ }
+ }
+
+ @Test
+ public void testInstantPerfettoWithArgs() {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
+ .addArg("foo", "bar")
+ .addFlow(1)
+ .emit();
+
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ PerfettoTrace.instant(FOO_CATEGORY, "testInstantP")
+ .addArg("foo", "bar")
+ .addFlow(1)
+ .emit();
+ }
+ }
+
+ private static TraceConfig getTraceConfig(String cat) {
+ BufferConfig bufferConfig = BufferConfig.newBuilder().setSizeKb(1024).build();
+ TrackEventConfig trackEventConfig = TrackEventConfig
+ .newBuilder()
+ .addEnabledCategories(cat)
+ .build();
+ DataSourceConfig dsConfig = DataSourceConfig
+ .newBuilder()
+ .setName("track_event")
+ .setTargetBuffer(0)
+ .setTrackEventConfig(trackEventConfig)
+ .build();
+ DataSource ds = DataSource.newBuilder().setConfig(dsConfig).build();
+ TraceConfig traceConfig = TraceConfig
+ .newBuilder()
+ .addBuffers(bufferConfig)
+ .addDataSources(ds)
+ .build();
+ return traceConfig;
+ }
}
diff --git a/core/api/current.txt b/core/api/current.txt
index 17e7d7a..a893fa5 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -20785,6 +20785,7 @@
method public void registerDisplayListener(android.hardware.display.DisplayManager.DisplayListener, android.os.Handler);
method @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public void registerDisplayListener(@NonNull java.util.concurrent.Executor, long, @NonNull android.hardware.display.DisplayManager.DisplayListener);
method public void unregisterDisplayListener(android.hardware.display.DisplayManager.DisplayListener);
+ field @FlaggedApi("com.android.server.display.feature.flags.display_category_built_in") public static final String DISPLAY_CATEGORY_BUILT_IN_DISPLAYS = "android.hardware.display.category.BUILT_IN_DISPLAYS";
field public static final String DISPLAY_CATEGORY_PRESENTATION = "android.hardware.display.category.PRESENTATION";
field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_ADDED = 1L; // 0x1L
field @FlaggedApi("com.android.server.display.feature.flags.display_listener_performance_improvements") public static final long EVENT_TYPE_DISPLAY_CHANGED = 4L; // 0x4L
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7c1c868..85ab5ed 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1727,6 +1727,7 @@
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public void setShouldAlwaysRespectAppRequestedMode(boolean);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setUserDisabledHdrTypes(@NonNull int[]);
method @RequiresPermission(android.Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS) public boolean shouldAlwaysRespectAppRequestedMode();
+ field public static final String DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED = "android.hardware.display.category.ALL_INCLUDING_DISABLED";
field public static final String DISPLAY_CATEGORY_REAR = "android.hardware.display.category.REAR";
field public static final String HDR_OUTPUT_CONTROL_FLAG = "enable_hdr_output_control";
field public static final int SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS = 2; // 0x2
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 0590a06..b7460e9 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -146,6 +146,22 @@
"android.hardware.display.category.PRESENTATION";
/**
+ * Display category: Built in displays.
+ *
+ * <p>
+ * This category can be used to identify displays that are built into the device. The
+ * displays that are returned may be inactive or disabled at the current moment. The
+ * returned displays are useful in identifying the various sizes of built-in displays. The
+ * id from {@link Display#getDisplayId()} is not guaranteed to be stable and may change
+ * when the display becomes active.
+ * </p>
+ * @see #getDisplays(String)
+ */
+ @FlaggedApi(com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_CATEGORY_BUILT_IN)
+ public static final String DISPLAY_CATEGORY_BUILT_IN_DISPLAYS =
+ "android.hardware.display.category.BUILT_IN_DISPLAYS";
+
+ /**
* Display category: Rear displays.
* <p>
* This category can be used to identify complementary internal displays that are facing away
@@ -171,6 +187,8 @@
* @see #getDisplays(String)
* @hide
*/
+ @TestApi
+ @SuppressLint("UnflaggedApi")
public static final String DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED =
"android.hardware.display.category.ALL_INCLUDING_DISABLED";
@@ -623,6 +641,9 @@
* is triggered whenever the properties of a {@link android.view.Display}, such as size,
* state, density are modified.
*
+ * This event is not triggered for refresh rate changes as they can change very often.
+ * To monitor refresh rate changes, subscribe to {@link EVENT_TYPE_DISPLAY_REFRESH_RATE}.
+ *
* @see #registerDisplayListener(DisplayListener, Handler, long)
*
*/
@@ -729,10 +750,13 @@
* @see #DISPLAY_CATEGORY_PRESENTATION
*/
public Display[] getDisplays(String category) {
- boolean includeDisabled = (category != null
- && category.equals(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED));
+ boolean includeDisabled = shouldIncludeDisabledDisplays(category);
final int[] displayIds = mGlobal.getDisplayIds(includeDisabled);
- if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
+ if (Flags.displayCategoryBuiltIn()
+ && DISPLAY_CATEGORY_BUILT_IN_DISPLAYS.equals(category)) {
+ Display[] value = getDisplays(displayIds, DisplayManager::isBuiltInDisplay);
+ return value;
+ } else if (DISPLAY_CATEGORY_PRESENTATION.equals(category)) {
return getDisplays(displayIds, DisplayManager::isPresentationDisplay);
} else if (DISPLAY_CATEGORY_REAR.equals(category)) {
return getDisplays(displayIds, DisplayManager::isRearDisplay);
@@ -742,6 +766,16 @@
return new Display[0];
}
+ private boolean shouldIncludeDisabledDisplays(@Nullable String category) {
+ if (DISPLAY_CATEGORY_BUILT_IN_DISPLAYS.equals(category)) {
+ return true;
+ }
+ if (DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED.equals(category)) {
+ return true;
+ }
+ return false;
+ }
+
private Display[] getDisplays(int[] displayIds, Predicate<Display> predicate) {
ArrayList<Display> tmpDisplays = new ArrayList<>();
for (int displayId : displayIds) {
@@ -753,6 +787,13 @@
return tmpDisplays.toArray(new Display[tmpDisplays.size()]);
}
+ private static boolean isBuiltInDisplay(@Nullable Display display) {
+ if (display == null) {
+ return false;
+ }
+ return display.getType() == Display.TYPE_INTERNAL;
+ }
+
private static boolean isPresentationDisplay(@Nullable Display display) {
if (display == null || (display.getDisplayId() == DEFAULT_DISPLAY)
|| (display.getFlags() & Display.FLAG_PRESENTATION) == 0) {
@@ -801,6 +842,9 @@
* Registers a display listener to receive notifications about when
* displays are added, removed or changed.
*
+ * We encourage to use {@link #registerDisplayListener(Executor, long, DisplayListener)}
+ * instead to subscribe for explicit events of interest
+ *
* @param listener The listener to register.
* @param handler The handler on which the listener should be invoked, or null
* if the listener should be invoked on the calling thread's looper.
@@ -809,7 +853,9 @@
*/
public void registerDisplayListener(DisplayListener listener, Handler handler) {
registerDisplayListener(listener, handler, EVENT_TYPE_DISPLAY_ADDED
- | EVENT_TYPE_DISPLAY_CHANGED | EVENT_TYPE_DISPLAY_REMOVED);
+ | EVENT_TYPE_DISPLAY_CHANGED
+ | EVENT_TYPE_DISPLAY_REFRESH_RATE
+ | EVENT_TYPE_DISPLAY_REMOVED);
}
/**
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index b5715ed..339dbf2 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -1766,29 +1766,23 @@
}
if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_CHANGED) != 0) {
- // For backward compatibility, a client subscribing to
- // DisplayManager.EVENT_FLAG_DISPLAY_CHANGED will be enrolled to both Basic and
- // RR changes
- baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED
- | INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED;
}
- if ((eventFlags
- & DisplayManager.EVENT_TYPE_DISPLAY_REMOVED) != 0) {
+ if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_REMOVED) != 0) {
baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REMOVED;
}
- if (Flags.displayListenerPerformanceImprovements()) {
- if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_REFRESH_RATE) != 0) {
- baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
- }
+ if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_REFRESH_RATE) != 0) {
+ baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_REFRESH_RATE;
+ }
+ if (Flags.displayListenerPerformanceImprovements()) {
if ((eventFlags & DisplayManager.EVENT_TYPE_DISPLAY_STATE) != 0) {
baseEventMask |= INTERNAL_EVENT_FLAG_DISPLAY_STATE;
}
}
-
return baseEventMask;
}
}
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index cd48047..af40188 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -35,7 +35,6 @@
import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
import static com.android.hardware.input.Flags.useKeyGestureEventHandlerMultiKeyGestures;
import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
-import static com.android.input.flags.Flags.enableInputFilterRustImpl;
import static com.android.input.flags.Flags.keyboardRepeatKeys;
import android.Manifest;
@@ -883,7 +882,7 @@
* @hide
*/
public static boolean isAccessibilityBounceKeysFeatureEnabled() {
- return keyboardA11yBounceKeysFlag() && enableInputFilterRustImpl();
+ return keyboardA11yBounceKeysFlag();
}
/**
@@ -967,7 +966,7 @@
* @hide
*/
public static boolean isAccessibilitySlowKeysFeatureFlagEnabled() {
- return keyboardA11ySlowKeysFlag() && enableInputFilterRustImpl();
+ return keyboardA11ySlowKeysFlag();
}
/**
@@ -1053,7 +1052,7 @@
* @hide
*/
public static boolean isAccessibilityStickyKeysFeatureEnabled() {
- return keyboardA11yStickyKeysFlag() && enableInputFilterRustImpl();
+ return keyboardA11yStickyKeysFlag();
}
/**
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index 79323bf..ae017e8 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -57,16 +57,6 @@
}
flag {
- namespace: "input_native"
- name: "keyboard_layout_manager_multi_user_ime_setup"
- description: "Update KeyboardLayoutManager to work correctly with multi-user IME setup"
- bug: "354333072"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
-}
-
-flag {
name: "modifier_shortcut_dump"
namespace: "input"
description: "Dump keyboard shortcuts in dumpsys window"
diff --git a/core/java/android/os/PerfettoTrace.java b/core/java/android/os/PerfettoTrace.java
index e3f251e..68f1570 100644
--- a/core/java/android/os/PerfettoTrace.java
+++ b/core/java/android/os/PerfettoTrace.java
@@ -154,14 +154,44 @@
}
}
+ /**
+ * Manages a perfetto tracing session.
+ * Constructing this object with a config automatically starts a tracing session. Each session
+ * must be closed after use and then the resulting trace bytes can be read.
+ *
+ * The session could be in process or system wide, depending on {@code isBackendInProcess}.
+ * This functionality is intended for testing.
+ */
+ public static final class Session {
+ private final long mPtr;
+
+ /**
+ * Session ctor.
+ */
+ public Session(boolean isBackendInProcess, byte[] config) {
+ mPtr = native_start_session(isBackendInProcess, config);
+ }
+
+ /**
+ * Closes the session and returns the trace.
+ */
+ public byte[] close() {
+ return native_stop_session(mPtr);
+ }
+ }
+
@CriticalNative
private static native long native_get_process_track_uuid();
-
@CriticalNative
private static native long native_get_thread_track_uuid(long tid);
@FastNative
private static native void native_activate_trigger(String name, int ttlMs);
+ @FastNative
+ private static native void native_register(boolean isBackendInProcess);
+
+ private static native long native_start_session(boolean isBackendInProcess, byte[] config);
+ private static native byte[] native_stop_session(long ptr);
/**
* Writes a trace message to indicate a given section of code was invoked.
@@ -307,7 +337,7 @@
/**
* Registers the process with Perfetto.
*/
- public static void register() {
- Trace.registerWithPerfetto();
+ public static void register(boolean isBackendInProcess) {
+ native_register(isBackendInProcess);
}
}
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 2a5666c..e769abe 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -624,6 +624,7 @@
WAKE_REASON_TAP,
WAKE_REASON_LIFT,
WAKE_REASON_BIOMETRIC,
+ WAKE_REASON_DOCK,
})
@Retention(RetentionPolicy.SOURCE)
public @interface WakeReason{}
@@ -765,6 +766,12 @@
public static final int WAKE_REASON_BIOMETRIC = 17;
/**
+ * Wake up reason code: Waking up due to a user docking the device.
+ * @hide
+ */
+ public static final int WAKE_REASON_DOCK = 18;
+
+ /**
* Convert the wake reason to a string for debugging purposes.
* @hide
*/
@@ -788,6 +795,7 @@
case WAKE_REASON_TAP: return "WAKE_REASON_TAP";
case WAKE_REASON_LIFT: return "WAKE_REASON_LIFT";
case WAKE_REASON_BIOMETRIC: return "WAKE_REASON_BIOMETRIC";
+ case WAKE_REASON_DOCK: return "WAKE_REASON_DOCK";
default: return Integer.toString(wakeReason);
}
}
diff --git a/core/java/android/os/Trace.java b/core/java/android/os/Trace.java
index 4a37e0a..09e6a45 100644
--- a/core/java/android/os/Trace.java
+++ b/core/java/android/os/Trace.java
@@ -164,8 +164,6 @@
private static native void nativeInstant(long tag, String name);
@FastNative
private static native void nativeInstantForTrack(long tag, String trackName, String name);
- @FastNative
- private static native void nativeRegisterWithPerfetto();
private Trace() {
}
@@ -545,6 +543,6 @@
* @hide
*/
public static void registerWithPerfetto() {
- nativeRegisterWithPerfetto();
+ PerfettoTrace.register(false /* isBackendInProcess */);
}
}
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index bd6ff4c..ae0e9c6 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -532,14 +532,30 @@
public static final int FLAG_NO_FOCUS_CHANGE = MotionEventFlag.NO_FOCUS_CHANGE;
/**
- * This flag indicates that this event was modified by or generated from an accessibility
- * service. Value = 0x800
+ * This flag indicates that this event was injected from some
+ * {@link android.accessibilityservice.AccessibilityService}, which may be either an
+ * Accessibility Tool OR a service using that API for purposes other than assisting users with
+ * disabilities. Value = 0x800
+ * @see #FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL
* @hide
*/
@TestApi
public static final int FLAG_IS_ACCESSIBILITY_EVENT = MotionEventFlag.IS_ACCESSIBILITY_EVENT;
/**
+ * This flag indicates that this event was injected from an
+ * {@link android.accessibilityservice.AccessibilityService} with the
+ * {@link android.accessibilityservice.AccessibilityServiceInfo#isAccessibilityTool()} property
+ * set to true. These services (known as "Accessibility Tools") are used to assist users with
+ * disabilities, so events from these services should be able to reach all Views including
+ * Views which set {@link View#isAccessibilityDataSensitive()} to true.
+ * Value = 0x1000
+ * @hide
+ */
+ public static final int FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL =
+ MotionEventFlag.INJECTED_FROM_ACCESSIBILITY_TOOL;
+
+ /**
* Private flag that indicates when the system has detected that this motion event
* may be inconsistent with respect to the sequence of previously delivered motion events,
* such as when a pointer move event is sent but the pointer is not down.
@@ -2534,6 +2550,24 @@
: flags & ~FLAG_TARGET_ACCESSIBILITY_FOCUS);
}
+ /**
+ * @see #FLAG_IS_ACCESSIBILITY_EVENT
+ * @hide
+ */
+ public boolean isInjectedFromAccessibilityService() {
+ final int flags = getFlags();
+ return (flags & FLAG_IS_ACCESSIBILITY_EVENT) != 0;
+ }
+
+ /**
+ * @see #FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL
+ * @hide
+ */
+ public boolean isInjectedFromAccessibilityTool() {
+ final int flags = getFlags();
+ return (flags & FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL) != 0;
+ }
+
/** @hide */
public final boolean isHoverExitPending() {
final int flags = getFlags();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7206906..0866e0d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16654,6 +16654,16 @@
// Window is obscured, drop this touch.
return false;
}
+ if (android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+ if (event.isInjectedFromAccessibilityService()
+ // If the event came from an Accessibility Service that does *not* declare
+ // itself as AccessibilityServiceInfo#isAccessibilityTool and this View is
+ // declared sensitive then drop the event.
+ // Only Accessibility Tools are allowed to interact with sensitive Views.
+ && !event.isInjectedFromAccessibilityTool() && isAccessibilityDataSensitive()) {
+ return false;
+ }
+ }
return true;
}
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 6d2c0d00..bb8958b 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -17,6 +17,7 @@
package android.view;
import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
+import static android.os.IInputConstants.POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
import static android.os.IInputConstants.POLICY_FLAG_KEY_GESTURE_TRIGGERED;
import android.annotation.IntDef;
@@ -37,6 +38,7 @@
int FLAG_VIRTUAL = 0x00000002;
int FLAG_INJECTED_FROM_ACCESSIBILITY = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY;
+ int FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL = POLICY_FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
int FLAG_KEY_GESTURE_TRIGGERED = POLICY_FLAG_KEY_GESTURE_TRIGGERED;
int FLAG_INJECTED = 0x01000000;
int FLAG_TRUSTED = 0x02000000;
diff --git a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
index 294e5da..37f393e 100644
--- a/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
+++ b/core/java/android/view/accessibility/flags/accessibility_flags.aconfig
@@ -4,6 +4,14 @@
# NOTE: Keep alphabetized to help limit merge conflicts from multiple simultaneous editors.
flag {
+ name: "a11y_character_in_window_api"
+ namespace: "accessibility"
+ description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates."
+ bug: "375429616"
+ is_exported: true
+}
+
+flag {
name: "a11y_expansion_state_api"
namespace: "accessibility"
description: "Enables new APIs for an app to convey if a node is expanded or collapsed."
@@ -42,23 +50,15 @@
}
flag {
- name: "a11y_character_in_window_api"
- namespace: "accessibility"
- description: "Enables new extra data key for an AccessibilityService to request character bounds in unmagnified window coordinates."
- bug: "375429616"
- is_exported: true
-}
-
-flag {
- namespace: "accessibility"
name: "allow_shortcut_chooser_on_lockscreen"
+ namespace: "accessibility"
description: "Allows the a11y shortcut disambig dialog to appear on the lockscreen"
bug: "303871725"
}
flag {
- namespace: "accessibility"
name: "braille_display_hid"
+ namespace: "accessibility"
is_exported: true
description: "Enables new APIs for an AccessibilityService to communicate with a HID Braille display"
bug: "303522222"
@@ -72,47 +72,62 @@
}
flag {
- namespace: "accessibility"
name: "collection_info_item_counts"
+ namespace: "accessibility"
is_exported: true
description: "Fields for total items and the number of important for accessibility items in a collection"
bug: "302376158"
}
flag {
- namespace: "accessibility"
name: "copy_events_for_gesture_detection"
+ namespace: "accessibility"
description: "Creates copies of MotionEvents and GestureEvents in GestureMatcher"
bug: "280130713"
}
flag {
- namespace: "accessibility"
name: "deprecate_accessibility_announcement_apis"
+ namespace: "accessibility"
description: "Controls the deprecation of platform APIs related to disruptive accessibility announcements"
bug: "376727542"
is_exported: true
}
flag {
- namespace: "accessibility"
name: "deprecate_ani_label_for_apis"
+ namespace: "accessibility"
description: "Controls the deprecation of AccessibilityNodeInfo labelFor apis"
bug: "333783827"
is_exported: true
}
flag {
+ name: "enable_system_pinch_zoom_gesture"
namespace: "accessibility"
+ description: "Feature flag for system pinch zoom gesture detector and related opt-out apis"
+ bug: "283323770"
+}
+
+flag {
+ name: "enable_type_window_control"
+ namespace: "accessibility"
+ is_exported: true
+ description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
+ bug: "320445550"
+}
+
+flag {
name: "flash_notification_system_api"
+ namespace: "accessibility"
is_exported: true
description: "Makes flash notification APIs as system APIs for calling from mainline module"
bug: "303131332"
}
flag {
- namespace: "accessibility"
name: "focus_rect_min_size"
+ namespace: "accessibility"
description: "Ensures the a11y focus rect is big enough to be drawn as visible"
bug: "368667566"
metadata {
@@ -121,13 +136,45 @@
}
flag {
- namespace: "accessibility"
name: "force_invert_color"
+ namespace: "accessibility"
description: "Enable force force-dark for smart inversion and dark theme everywhere"
bug: "282821643"
}
flag {
+ name: "global_action_media_play_pause"
+ namespace: "accessibility"
+ description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE"
+ bug: "334954140"
+ is_exported: true
+}
+
+flag {
+ name: "global_action_menu"
+ namespace: "accessibility"
+ description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU"
+ bug: "334954140"
+ is_exported: true
+}
+
+flag {
+ name: "granular_scrolling"
+ namespace: "accessibility"
+ is_exported: true
+ description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
+ bug: "302376158"
+}
+
+flag {
+ name: "indeterminate_range_info"
+ namespace: "accessibility"
+ description: "Creates a way to create an INDETERMINATE RangeInfo"
+ bug: "376108874"
+ is_exported: true
+}
+
+flag {
name: "migrate_enable_shortcuts"
namespace: "accessibility"
description: "Refactors deprecated code to use AccessibilityManager#enableShortcutsForTargets."
@@ -146,70 +193,13 @@
}
flag {
+ name: "prevent_a11y_nontool_from_injecting_into_sensitive_views"
namespace: "accessibility"
- name: "global_action_menu"
- description: "Allow AccessibilityService to perform GLOBAL_ACTION_MENU"
- bug: "334954140"
- is_exported: true
-}
-
-flag {
- namespace: "accessibility"
- name: "global_action_media_play_pause"
- description: "Allow AccessibilityService to perform GLOBAL_ACTION_MEDIA_PLAY_PAUSE"
- bug: "334954140"
- is_exported: true
-}
-
-flag {
- namespace: "accessibility"
- name: "granular_scrolling"
- is_exported: true
- description: "Allow the use of granular scrolling. This allows scrollable nodes to scroll by increments other than a full screen"
- bug: "302376158"
-}
-
-flag {
- namespace: "accessibility"
- name: "reduce_window_content_changed_event_throttle"
- description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED"
- bug: "277305460"
-}
-
-flag {
- namespace: "accessibility"
- name: "remove_child_hover_check_for_touch_exploration"
- description: "Remove a check for a hovered child that prevents touch events from being delegated to non-direct descendants"
- bug: "304770837"
-}
-
-flag {
- name: "skip_accessibility_warning_dialog_for_trusted_services"
- namespace: "accessibility"
- description: "Skips showing the accessibility warning dialog for trusted services."
- bug: "303511250"
-}
-
-flag {
- namespace: "accessibility"
- name: "enable_type_window_control"
- is_exported: true
- description: "adds new TYPE_WINDOW_CONTROL to AccessibilityWindowInfo for detecting Window Decorations"
- bug: "320445550"
-}
-
-flag {
- namespace: "accessibility"
- name: "update_always_on_a11y_service"
- description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
- bug: "298869916"
-}
-
-flag {
- name: "enable_system_pinch_zoom_gesture"
- namespace: "accessibility"
- description: "Feature flag for system pinch zoom gesture detector and related opt-out apis"
- bug: "283323770"
+ description: "Prevents injected gestures from A11yServices without isAccessibilityTool=true from reaching AccessibilityDataSensitive UI elements"
+ bug: "284180538"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -223,6 +213,30 @@
}
flag {
+ name: "reduce_window_content_changed_event_throttle"
+ namespace: "accessibility"
+ description: "Reduces the throttle of AccessibilityEvent of TYPE_WINDOW_CONTENT_CHANGED"
+ bug: "277305460"
+}
+
+flag {
+ name: "remove_child_hover_check_for_touch_exploration"
+ namespace: "accessibility"
+ description: "Remove a check for a hovered child that prevents touch events from being delegated to non-direct descendants"
+ bug: "304770837"
+}
+
+flag {
+ name: "restore_a11y_secure_settings_on_hsum_device"
+ namespace: "accessibility"
+ description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user"
+ bug: "381294327"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "restore_a11y_shortcut_target_service"
namespace: "accessibility"
description: "Perform merging and other bug fixes for SettingsProvider restore of ACCESSIBILITY_SHORTCUT_TARGET_SERVICES secure setting"
@@ -233,13 +247,10 @@
}
flag {
- name: "restore_a11y_secure_settings_on_hsum_device"
+ name: "skip_accessibility_warning_dialog_for_trusted_services"
namespace: "accessibility"
- description: "Grab the a11y settings and send the settings restored broadcast for current visible foreground user"
- bug: "381294327"
- metadata {
- purpose: PURPOSE_BUGFIX
- }
+ description: "Skips showing the accessibility warning dialog for trusted services."
+ bug: "303511250"
}
flag {
@@ -274,6 +285,13 @@
}
flag {
+ namespace: "accessibility"
+ name: "update_always_on_a11y_service"
+ description: "Updates the Always-On A11yService state when the user changes the enablement of the shortcut."
+ bug: "298869916"
+}
+
+flag {
name: "warning_use_default_dialog_type"
namespace: "accessibility"
description: "Uses the default type for the A11yService warning dialog, instead of SYSTEM_ALERT_DIALOG"
@@ -282,11 +300,3 @@
purpose: PURPOSE_BUGFIX
}
}
-
-flag {
- name: "indeterminate_range_info"
- namespace: "accessibility"
- description: "Creates a way to create an INDETERMINATE RangeInfo"
- bug: "376108874"
- is_exported: true
-}
diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java
index 3f2aa1c..2918610 100644
--- a/core/java/android/window/DesktopModeFlags.java
+++ b/core/java/android/window/DesktopModeFlags.java
@@ -148,28 +148,22 @@
return isFlagTrue(mFlagFunction, mShouldOverrideByDevOption);
}
+ public static boolean isDesktopModeForcedEnabled() {
+ return getToggleOverride() == ToggleOverride.OVERRIDE_ON;
+ }
+
private static boolean isFlagTrue(BooleanSupplier flagFunction,
boolean shouldOverrideByDevOption) {
if (!shouldOverrideByDevOption) return flagFunction.getAsBoolean();
if (Flags.showDesktopExperienceDevOption()) {
- return switch (getToggleOverride(null)) {
+ return switch (getToggleOverride()) {
case OVERRIDE_UNSET, OVERRIDE_OFF -> flagFunction.getAsBoolean();
case OVERRIDE_ON -> true;
};
}
if (Flags.showDesktopWindowingDevOption()) {
- Application application = ActivityThread.currentApplication();
- if (application == null) {
- Log.w(TAG, "Could not get the current application.");
- return flagFunction.getAsBoolean();
- }
- ContentResolver contentResolver = application.getContentResolver();
- if (contentResolver == null) {
- Log.w(TAG, "Could not get the content resolver for the application.");
- return flagFunction.getAsBoolean();
- }
boolean shouldToggleBeEnabledByDefault = Flags.enableDesktopWindowingMode();
- return switch (getToggleOverride(contentResolver)) {
+ return switch (getToggleOverride()) {
case OVERRIDE_UNSET -> flagFunction.getAsBoolean();
// When toggle override matches its default state, don't override flags. This
// helps users reset their feature overrides.
@@ -180,14 +174,13 @@
return flagFunction.getAsBoolean();
}
- private static ToggleOverride getToggleOverride(@Nullable ContentResolver contentResolver) {
+ private static ToggleOverride getToggleOverride() {
// If cached, return it
if (sCachedToggleOverride != null) {
return sCachedToggleOverride;
}
-
// Otherwise, fetch and cache it
- ToggleOverride override = getToggleOverrideFromSystem(contentResolver);
+ ToggleOverride override = getToggleOverrideFromSystem();
sCachedToggleOverride = override;
Log.d(TAG, "Toggle override initialized to: " + override);
return override;
@@ -196,8 +189,7 @@
/**
* Returns {@link ToggleOverride} from Settings.Global set by toggle.
*/
- private static ToggleOverride getToggleOverrideFromSystem(
- @Nullable ContentResolver contentResolver) {
+ private static ToggleOverride getToggleOverrideFromSystem() {
int settingValue;
if (Flags.showDesktopExperienceDevOption()) {
settingValue = SystemProperties.getInt(
@@ -205,6 +197,16 @@
ToggleOverride.OVERRIDE_UNSET.getSetting()
);
} else {
+ final Application application = ActivityThread.currentApplication();
+ if (application == null) {
+ Log.w(TAG, "Could not get the current application.");
+ return ToggleOverride.OVERRIDE_UNSET;
+ }
+ final ContentResolver contentResolver = application.getContentResolver();
+ if (contentResolver == null) {
+ Log.w(TAG, "Could not get the content resolver for the application.");
+ return ToggleOverride.OVERRIDE_UNSET;
+ }
settingValue = Settings.Global.getInt(
contentResolver,
Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES,
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index 51d488f..d20b067 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -600,3 +600,13 @@
description: "Enables split screen on non default displays"
bug: "384999213"
}
+
+flag {
+ name: "enable_desktop_mode_through_dev_option"
+ namespace: "lse_desktop_experience"
+ description: "Enables support for desktop mode through developer options for devices eligible for desktop mode."
+ bug: "382238347"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/core/java/com/android/internal/os/BaseCommand.java b/core/java/com/android/internal/os/BaseCommand.java
index c85b5d7..af763e4 100644
--- a/core/java/com/android/internal/os/BaseCommand.java
+++ b/core/java/com/android/internal/os/BaseCommand.java
@@ -58,15 +58,23 @@
mRawArgs = args;
mArgs.init(null, null, null, null, args, 0);
+ int status = 1;
try {
onRun();
+ status = 0;
} catch (IllegalArgumentException e) {
onShowUsage(System.err);
System.err.println();
System.err.println("Error: " + e.getMessage());
+ status = 0;
} catch (Exception e) {
e.printStackTrace(System.err);
- System.exit(1);
+ } finally {
+ System.out.flush();
+ System.err.flush();
+ }
+ if (status != 0) {
+ System.exit(status);
}
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 72cb9d1..98d1ef6 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -217,9 +217,9 @@
void setUdfpsRefreshRateCallback(in IUdfpsRefreshRateRequestCallback callback);
/**
- * Notifies System UI that the display is ready to show system decorations.
+ * Notifies System UI that the system decorations should be added on the display.
*/
- void onDisplayReady(int displayId);
+ void onDisplayAddSystemDecorations(int displayId);
/**
* Notifies System UI that the system decorations should be removed from the display.
diff --git a/core/jni/android_os_PerfettoTrace.cpp b/core/jni/android_os_PerfettoTrace.cpp
index 962aefc..9bedfa2 100644
--- a/core/jni/android_os_PerfettoTrace.cpp
+++ b/core/jni/android_os_PerfettoTrace.cpp
@@ -24,9 +24,12 @@
#include <nativehelper/scoped_primitive_array.h>
#include <nativehelper/scoped_utf_chars.h>
#include <nativehelper/utils.h>
+#include <tracing_perfetto.h>
#include <tracing_sdk.h>
namespace android {
+constexpr int kFlushTimeoutMs = 5000;
+
template <typename T>
inline static T* toPointer(jlong ptr) {
return reinterpret_cast<T*>(static_cast<uintptr_t>(ptr));
@@ -51,6 +54,10 @@
tracing_perfetto::activate_trigger(name_chars.c_str(), static_cast<uint32_t>(ttl_ms));
}
+void android_os_PerfettoTrace_register(bool is_backend_in_process) {
+ tracing_perfetto::registerWithPerfetto(is_backend_in_process);
+}
+
static jlong android_os_PerfettoTraceCategory_init(JNIEnv* env, jclass, jstring name, jstring tag,
jstring severity) {
ScopedUtfChars name_chars = GET_UTF_OR_RETURN(env, name);
@@ -85,6 +92,36 @@
return toJLong(category->get());
}
+static jlong android_os_PerfettoTrace_start_session(JNIEnv* env, jclass /* obj */,
+ jboolean is_backend_in_process,
+ jbyteArray config_bytes) {
+ jsize length = env->GetArrayLength(config_bytes);
+ std::vector<uint8_t> data;
+ data.reserve(length);
+ env->GetByteArrayRegion(config_bytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
+
+ tracing_perfetto::Session* session =
+ new tracing_perfetto::Session(is_backend_in_process, data.data(), length);
+
+ return reinterpret_cast<long>(session);
+}
+
+static jbyteArray android_os_PerfettoTrace_stop_session([[maybe_unused]] JNIEnv* env,
+ jclass /* obj */, jlong ptr) {
+ tracing_perfetto::Session* session = reinterpret_cast<tracing_perfetto::Session*>(ptr);
+
+ session->FlushBlocking(kFlushTimeoutMs);
+ session->StopBlocking();
+
+ std::vector<uint8_t> data = session->ReadBlocking();
+
+ delete session;
+
+ jbyteArray bytes = env->NewByteArray(data.size());
+ env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
+ return bytes;
+}
+
static const JNINativeMethod gCategoryMethods[] = {
{"native_init", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)J",
(void*)android_os_PerfettoTraceCategory_init},
@@ -101,7 +138,10 @@
{"native_get_thread_track_uuid", "(J)J",
(void*)android_os_PerfettoTrace_get_thread_track_uuid},
{"native_activate_trigger", "(Ljava/lang/String;I)V",
- (void*)android_os_PerfettoTrace_activate_trigger}};
+ (void*)android_os_PerfettoTrace_activate_trigger},
+ {"native_register", "(Z)V", (void*)android_os_PerfettoTrace_register},
+ {"native_start_session", "(Z[B)J", (void*)android_os_PerfettoTrace_start_session},
+ {"native_stop_session", "(J)[B", (void*)android_os_PerfettoTrace_stop_session}};
int register_android_os_PerfettoTrace(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "android/os/PerfettoTrace", gTraceMethods,
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 21e056d..50618c5 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -131,10 +131,6 @@
return tracing_perfetto::isTagEnabled(tag);
}
-static void android_os_Trace_nativeRegisterWithPerfetto(JNIEnv* env) {
- tracing_perfetto::registerWithPerfetto();
-}
-
static const JNINativeMethod gTraceMethods[] = {
/* name, signature, funcPtr */
{"nativeSetAppTracingAllowed", "(Z)V", (void*)android_os_Trace_nativeSetAppTracingAllowed},
@@ -157,7 +153,6 @@
{"nativeInstant", "(JLjava/lang/String;)V", (void*)android_os_Trace_nativeInstant},
{"nativeInstantForTrack", "(JLjava/lang/String;Ljava/lang/String;)V",
(void*)android_os_Trace_nativeInstantForTrack},
- {"nativeRegisterWithPerfetto", "()V", (void*)android_os_Trace_nativeRegisterWithPerfetto},
// ----------- @CriticalNative ----------------
{"nativeIsTagEnabled", "(J)Z", (void*)android_os_Trace_nativeIsTagEnabled},
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 586cafd..a49e034 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -7245,6 +7245,9 @@
<!-- Whether desktop mode is supported on the current device -->
<bool name="config_isDesktopModeSupported">false</bool>
+ <!-- Whether the developer option for desktop mode is supported on the current device -->
+ <bool name="config_isDesktopModeDevOptionSupported">false</bool>
+
<!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. -->
<integer name="config_maxDesktopWindowingActiveTasks">0</integer>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 772a741..aca9d30 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -5709,6 +5709,9 @@
<!-- Whether desktop mode is supported on the current device -->
<java-symbol type="bool" name="config_isDesktopModeSupported" />
+ <!-- Whether the developer option for desktop mode is supported on the current device -->
+ <java-symbol type="bool" name="config_isDesktopModeDevOptionSupported" />
+
<!-- Maximum number of active tasks on a given Desktop Windowing session. Set to 0 for unlimited. -->
<java-symbol type="integer" name="config_maxDesktopWindowingActiveTasks"/>
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 1b6746c..c06ad64 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -122,7 +122,6 @@
"android.view.flags-aconfig-java",
],
jni_libs: [
- "libperfetto_trace_test_jni",
"libpowermanagertest_jni",
"libviewRootImplTest_jni",
"libworksourceparceltest_jni",
diff --git a/core/tests/coretests/jni/Android.bp b/core/tests/coretests/jni/Android.bp
index 798ec90..d6379ca 100644
--- a/core/tests/coretests/jni/Android.bp
+++ b/core/tests/coretests/jni/Android.bp
@@ -111,27 +111,3 @@
],
gtest: false,
}
-
-cc_test_library {
- name: "libperfetto_trace_test_jni",
- srcs: [
- "PerfettoTraceTest.cpp",
- ],
- static_libs: [
- "perfetto_trace_protos",
- "libtracing_perfetto_test_utils",
- ],
- shared_libs: [
- "liblog",
- "libnativehelper",
- "libperfetto_c",
- "libprotobuf-cpp-lite",
- "libtracing_perfetto",
- ],
- stl: "libc++_static",
- cflags: [
- "-Werror",
- "-Wall",
- ],
- gtest: false,
-}
diff --git a/core/tests/coretests/jni/PerfettoTraceTest.cpp b/core/tests/coretests/jni/PerfettoTraceTest.cpp
deleted file mode 100644
index 41d02ed7..0000000
--- a/core/tests/coretests/jni/PerfettoTraceTest.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * 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.
- */
-
-// #define LOG_NDEBUG 0
-#define LOG_TAG "PerfettoTraceTest"
-
-#include <nativehelper/JNIHelp.h>
-#include <utils/Log.h>
-
-#include "jni.h"
-#include "perfetto/public/abi/data_source_abi.h"
-#include "perfetto/public/abi/heap_buffer.h"
-#include "perfetto/public/abi/pb_decoder_abi.h"
-#include "perfetto/public/abi/tracing_session_abi.h"
-#include "perfetto/public/abi/track_event_abi.h"
-#include "perfetto/public/compiler.h"
-#include "perfetto/public/data_source.h"
-#include "perfetto/public/pb_decoder.h"
-#include "perfetto/public/producer.h"
-#include "perfetto/public/protos/config/trace_config.pzc.h"
-#include "perfetto/public/protos/trace/interned_data/interned_data.pzc.h"
-#include "perfetto/public/protos/trace/test_event.pzc.h"
-#include "perfetto/public/protos/trace/trace.pzc.h"
-#include "perfetto/public/protos/trace/trace_packet.pzc.h"
-#include "perfetto/public/protos/trace/track_event/debug_annotation.pzc.h"
-#include "perfetto/public/protos/trace/track_event/track_descriptor.pzc.h"
-#include "perfetto/public/protos/trace/track_event/track_event.pzc.h"
-#include "perfetto/public/protos/trace/trigger.pzc.h"
-#include "perfetto/public/te_category_macros.h"
-#include "perfetto/public/te_macros.h"
-#include "perfetto/public/track_event.h"
-#include "protos/perfetto/trace/interned_data/interned_data.pb.h"
-#include "protos/perfetto/trace/trace.pb.h"
-#include "protos/perfetto/trace/trace_packet.pb.h"
-#include "tracing_perfetto.h"
-#include "utils.h"
-
-namespace android {
-using ::perfetto::protos::EventCategory;
-using ::perfetto::protos::EventName;
-using ::perfetto::protos::FtraceEvent;
-using ::perfetto::protos::FtraceEventBundle;
-using ::perfetto::protos::InternedData;
-using ::perfetto::protos::Trace;
-using ::perfetto::protos::TracePacket;
-
-using ::perfetto::shlib::test_utils::TracingSession;
-
-struct TracingSessionHolder {
- TracingSession tracing_session;
-};
-
-static void nativeRegisterPerfetto([[maybe_unused]] JNIEnv* env, jclass /* obj */) {
- tracing_perfetto::registerWithPerfetto(false /* test */);
-}
-
-static jlong nativeStartTracing(JNIEnv* env, jclass /* obj */, jbyteArray configBytes) {
- jsize length = env->GetArrayLength(configBytes);
- std::vector<uint8_t> data;
- data.reserve(length);
- env->GetByteArrayRegion(configBytes, 0, length, reinterpret_cast<jbyte*>(data.data()));
-
- TracingSession session = TracingSession::FromBytes(data.data(), length);
- TracingSessionHolder* holder = new TracingSessionHolder(std::move(session));
-
- return reinterpret_cast<long>(holder);
-}
-
-static jbyteArray nativeStopTracing([[maybe_unused]] JNIEnv* env, jclass /* obj */, jlong ptr) {
- TracingSessionHolder* holder = reinterpret_cast<TracingSessionHolder*>(ptr);
-
- // Stop
- holder->tracing_session.FlushBlocking(5000);
- holder->tracing_session.StopBlocking();
-
- std::vector<uint8_t> data = holder->tracing_session.ReadBlocking();
-
- delete holder;
-
- jbyteArray bytes = env->NewByteArray(data.size());
- env->SetByteArrayRegion(bytes, 0, data.size(), reinterpret_cast<jbyte*>(data.data()));
- return bytes;
-}
-
-extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) {
- JNIEnv* env;
- const JNINativeMethod methodTable[] = {/* name, signature, funcPtr */
- {"nativeStartTracing", "([B)J",
- (void*)nativeStartTracing},
- {"nativeStopTracing", "(J)[B", (void*)nativeStopTracing},
- {"nativeRegisterPerfetto", "()V",
- (void*)nativeRegisterPerfetto}};
-
- if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
- return JNI_ERR;
- }
-
- jniRegisterNativeMethods(env, "android/os/PerfettoTraceTest", methodTable,
- sizeof(methodTable) / sizeof(JNINativeMethod));
-
- return JNI_VERSION_1_6;
-}
-
-} /* namespace android */
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
index 8fa5103..dc2f0a6 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
+++ b/core/tests/coretests/src/android/hardware/display/DisplayManagerGlobalTest.java
@@ -307,8 +307,10 @@
assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_ADDED,
mDisplayManagerGlobal
.mapFiltersToInternalEventFlag(DisplayManager.EVENT_TYPE_DISPLAY_ADDED, 0));
- assertEquals(DISPLAY_CHANGE_EVENTS, mDisplayManagerGlobal
- .mapFiltersToInternalEventFlag(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED, 0));
+ assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_BASIC_CHANGED,
+ mDisplayManagerGlobal
+ .mapFiltersToInternalEventFlag(DisplayManager.EVENT_TYPE_DISPLAY_CHANGED,
+ 0));
assertEquals(DisplayManagerGlobal.INTERNAL_EVENT_FLAG_DISPLAY_REMOVED,
mDisplayManagerGlobal.mapFiltersToInternalEventFlag(
DisplayManager.EVENT_TYPE_DISPLAY_REMOVED, 0));
diff --git a/core/tests/coretests/src/android/os/PerfettoTraceTest.java b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
index ad28383..0b5a446 100644
--- a/core/tests/coretests/src/android/os/PerfettoTraceTest.java
+++ b/core/tests/coretests/src/android/os/PerfettoTraceTest.java
@@ -28,7 +28,6 @@
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.util.ArraySet;
-import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -84,19 +83,9 @@
private final Set<String> mDebugAnnotationNames = new ArraySet<>();
private final Set<String> mTrackNames = new ArraySet<>();
- static {
- try {
- System.loadLibrary("perfetto_trace_test_jni");
- Log.i(TAG, "Successfully loaded trace_test native library");
- } catch (UnsatisfiedLinkError ule) {
- Log.w(TAG, "Could not load trace_test native library");
- }
- }
-
@Before
public void setUp() {
- PerfettoTrace.register();
- nativeRegisterPerfetto();
+ PerfettoTrace.register(true);
FOO_CATEGORY.register();
mCategoryNames.clear();
@@ -110,7 +99,7 @@
public void testDebugAnnotations() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event")
.addFlow(2)
@@ -121,7 +110,7 @@
.addArg("string_val", FOO)
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -165,11 +154,11 @@
public void testDebugAnnotationsWithLambda() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event").addArg("long_val", 123L).emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -200,7 +189,7 @@
public void testNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.begin(FOO_CATEGORY, "event")
.usingNamedTrack(PerfettoTrace.getProcessTrackUuid(), FOO)
@@ -211,7 +200,7 @@
.usingNamedTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()), "bar")
.emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasTrackUuid = false;
@@ -248,7 +237,7 @@
public void testProcessThreadNamedTrack() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.begin(FOO_CATEGORY, "event")
.usingProcessNamedTrack(FOO)
@@ -259,7 +248,7 @@
.usingThreadNamedTrack(Process.myTid(), "%s-%s", "bar", "stool")
.emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasTrackUuid = false;
@@ -296,13 +285,13 @@
public void testCounterSimple() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.counter(FOO_CATEGORY, 16, FOO).emit();
PerfettoTrace.counter(FOO_CATEGORY, 3.14, "bar").emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasCounterValue = false;
@@ -339,7 +328,7 @@
public void testCounter() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.counter(FOO_CATEGORY, 16)
.usingCounterTrack(PerfettoTrace.getProcessTrackUuid(), FOO).emit();
@@ -348,7 +337,7 @@
.usingCounterTrack(PerfettoTrace.getThreadTrackUuid(Process.myTid()),
"%s-%s", "bar", "stool").emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasCounterValue = false;
@@ -385,14 +374,14 @@
public void testProcessThreadCounter() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.counter(FOO_CATEGORY, 16).usingProcessCounterTrack(FOO).emit();
PerfettoTrace.counter(FOO_CATEGORY, 3.14)
.usingThreadCounterTrack(Process.myTid(), "%s-%s", "bar", "stool").emit();
- Trace trace = Trace.parseFrom(nativeStopTracing(ptr));
+ Trace trace = Trace.parseFrom(session.close());
boolean hasTrackEvent = false;
boolean hasCounterValue = false;
@@ -429,7 +418,7 @@
public void testProto() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event_proto")
.beginProto()
@@ -441,7 +430,7 @@
.endProto()
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -477,7 +466,7 @@
public void testProtoNested() throws Exception {
TraceConfig traceConfig = getTraceConfig(FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event_proto_nested")
.beginProto()
@@ -494,7 +483,7 @@
.endProto()
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -538,13 +527,13 @@
public void testActivateTrigger() throws Exception {
TraceConfig traceConfig = getTriggerTraceConfig(FOO, FOO);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(FOO_CATEGORY, "event_trigger").emit();
PerfettoTrace.activateTrigger(FOO, 1000);
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -569,7 +558,7 @@
TraceConfig traceConfig = getTraceConfig(BAR);
Category barCategory = new Category(BAR);
- long ptr = nativeStartTracing(traceConfig.toByteArray());
+ PerfettoTrace.Session session = new PerfettoTrace.Session(true, traceConfig.toByteArray());
PerfettoTrace.instant(barCategory, "event")
.addArg("before", 1)
@@ -581,7 +570,7 @@
.addArg("after", 1)
.emit();
- byte[] traceBytes = nativeStopTracing(ptr);
+ byte[] traceBytes = session.close();
Trace trace = Trace.parseFrom(traceBytes);
@@ -603,10 +592,6 @@
assertThat(mDebugAnnotationNames).doesNotContain("before");
}
- private static native long nativeStartTracing(byte[] config);
- private static native void nativeRegisterPerfetto();
- private static native byte[] nativeStopTracing(long ptr);
-
private TrackEvent getTrackEvent(Trace trace, int idx) {
int curIdx = 0;
for (TracePacket packet: trace.getPacketList()) {
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
index 1ee71ca..e196880 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java
@@ -212,10 +212,18 @@
}
/**
+ * Return {@code true} if the current device supports the developer option for desktop mode.
+ */
+ private static boolean isDesktopModeDevOptionSupported(@NonNull Context context) {
+ return context.getResources().getBoolean(R.bool.config_isDesktopModeDevOptionSupported);
+ }
+
+ /**
* Return {@code true} if desktop mode dev option should be shown on current device
*/
public static boolean canShowDesktopModeDevOption(@NonNull Context context) {
- return isDeviceEligibleForDesktopMode(context) && Flags.showDesktopWindowingDevOption();
+ return isDeviceEligibleForDesktopModeDevOption(context)
+ && Flags.showDesktopWindowingDevOption();
}
/**
@@ -226,17 +234,25 @@
}
/** Returns if desktop mode dev option should be enabled if there is no user override. */
- public static boolean shouldDevOptionBeEnabledByDefault() {
- return Flags.enableDesktopWindowingMode();
+ public static boolean shouldDevOptionBeEnabledByDefault(Context context) {
+ return isDeviceEligibleForDesktopMode(context) && Flags.enableDesktopWindowingMode();
}
/**
* Return {@code true} if desktop mode is enabled and can be entered on the current device.
*/
public static boolean canEnterDesktopMode(@NonNull Context context) {
- if (!isDeviceEligibleForDesktopMode(context)) return false;
+ return (isDeviceEligibleForDesktopMode(context)
+ && DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue())
+ || isDesktopModeEnabledByDevOption(context);
+ }
- return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue();
+ /**
+ * Check if Desktop mode should be enabled because the dev option is shown and enabled.
+ */
+ private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) {
+ return DesktopModeFlags.isDesktopModeForcedEnabled()
+ && canShowDesktopModeDevOption(context);
}
/**
@@ -298,7 +314,21 @@
* Return {@code true} if desktop mode is unrestricted and is supported in the device.
*/
public static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
- return !enforceDeviceRestrictions() || isDesktopModeSupported(context);
+ return !enforceDeviceRestrictions() || isDesktopModeSupported(context) || (
+ Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionSupported(
+ context));
+ }
+
+ /**
+ * Return {@code true} if the developer option for desktop mode is unrestricted and is supported
+ * in the device.
+ *
+ * Note that, if {@link #isDeviceEligibleForDesktopMode(Context)} is true, then
+ * {@link #isDeviceEligibleForDesktopModeDevOption(Context)} is also true.
+ */
+ private static boolean isDeviceEligibleForDesktopModeDevOption(@NonNull Context context) {
+ return !enforceDeviceRestrictions() || isDesktopModeSupported(context)
+ || isDesktopModeDevOptionSupported(context);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index b09d324..10f8705 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -976,11 +976,13 @@
cascadeWindow(bounds, displayLayout, displayId)
}
val pendingIntent =
- PendingIntent.getActivity(
+ PendingIntent.getActivityAsUser(
context,
/* requestCode= */ 0,
intent,
PendingIntent.FLAG_IMMUTABLE,
+ /* options= */ null,
+ UserHandle.of(userId),
)
val ops =
ActivityOptions.fromBundle(options).apply {
@@ -1523,11 +1525,16 @@
private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
logV("addWallpaperActivity")
if (ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER.isTrue()) {
+
+ // If the wallpaper activity for this display already exists, let's reorder it to top.
+ val wallpaperActivityToken = desktopWallpaperActivityTokenProvider.getToken(displayId)
+ if (wallpaperActivityToken != null) {
+ wct.reorder(wallpaperActivityToken, /* onTop= */ true)
+ return
+ }
+
val intent = Intent(context, DesktopWallpaperActivity::class.java)
- if (
- desktopWallpaperActivityTokenProvider.getToken(displayId) == null &&
- Flags.enablePerDisplayDesktopWallpaperActivity()
- ) {
+ if (Flags.enablePerDisplayDesktopWallpaperActivity()) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
intent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
index 1eaae7e..9af2308 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/taskview/TaskViewTransitions.java
@@ -652,9 +652,16 @@
}
continue;
}
- startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
- finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl())
- .setPosition(chg.getLeash(), 0, 0);
+ final Rect boundsOnScreen = tv.prepareOpen(chg.getTaskInfo(), chg.getLeash());
+ if (boundsOnScreen != null) {
+ if (wct == null) wct = new WindowContainerTransaction();
+ updateBounds(tv, boundsOnScreen, startTransaction, finishTransaction,
+ chg.getTaskInfo(), chg.getLeash(), wct);
+ } else {
+ startTransaction.reparent(chg.getLeash(), tv.getSurfaceControl());
+ finishTransaction.reparent(chg.getLeash(), tv.getSurfaceControl())
+ .setPosition(chg.getLeash(), 0, 0);
+ }
changesHandled++;
}
}
@@ -683,30 +690,8 @@
WindowContainerTransaction wct) {
final Rect boundsOnScreen = taskView.prepareOpen(taskInfo, leash);
if (boundsOnScreen != null) {
- final SurfaceControl tvSurface = taskView.getSurfaceControl();
- // Surface is ready, so just reparent the task to this surface control
- startTransaction.reparent(leash, tvSurface)
- .show(leash);
- // Also reparent on finishTransaction since the finishTransaction will reparent back
- // to its "original" parent by default.
- if (finishTransaction != null) {
- finishTransaction.reparent(leash, tvSurface)
- .setPosition(leash, 0, 0)
- // TODO: maybe once b/280900002 is fixed this will be unnecessary
- .setWindowCrop(leash, boundsOnScreen.width(), boundsOnScreen.height());
- }
- if (useRepo()) {
- final TaskViewRepository.TaskViewState state = mTaskViewRepo.byTaskView(taskView);
- if (state != null) {
- state.mBounds.set(boundsOnScreen);
- state.mVisible = true;
- }
- } else {
- updateBoundsState(taskView, boundsOnScreen);
- updateVisibilityState(taskView, true /* visible */);
- }
- wct.setBounds(taskInfo.token, boundsOnScreen);
- taskView.applyCaptionInsetsIfNeeded();
+ updateBounds(taskView, boundsOnScreen, startTransaction, finishTransaction, taskInfo,
+ leash, wct);
} else {
// The surface has already been destroyed before the task has appeared,
// so go ahead and hide the task entirely
@@ -730,6 +715,36 @@
taskView.notifyAppeared(newTask);
}
+ private void updateBounds(TaskViewTaskController taskView, Rect boundsOnScreen,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction,
+ ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash,
+ WindowContainerTransaction wct) {
+ final SurfaceControl tvSurface = taskView.getSurfaceControl();
+ // Surface is ready, so just reparent the task to this surface control
+ startTransaction.reparent(leash, tvSurface)
+ .show(leash);
+ // Also reparent on finishTransaction since the finishTransaction will reparent back
+ // to its "original" parent by default.
+ if (finishTransaction != null) {
+ finishTransaction.reparent(leash, tvSurface)
+ .setPosition(leash, 0, 0)
+ .setWindowCrop(leash, boundsOnScreen.width(), boundsOnScreen.height());
+ }
+ if (useRepo()) {
+ final TaskViewRepository.TaskViewState state = mTaskViewRepo.byTaskView(taskView);
+ if (state != null) {
+ state.mBounds.set(boundsOnScreen);
+ state.mVisible = true;
+ }
+ } else {
+ updateBoundsState(taskView, boundsOnScreen);
+ updateVisibilityState(taskView, true /* visible */);
+ }
+ wct.setBounds(taskInfo.token, boundsOnScreen);
+ taskView.applyCaptionInsetsIfNeeded();
+ }
+
/** Interface for running an external transition in this object's pending queue. */
public interface ExternalTransition {
/** Starts a transition and returns an identifying key for lookup. */
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
index b5b7847..80e4c47a 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaExpandButtonTest.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.RequiresFlagsDisabled
import android.tools.flicker.junit.FlickerParametersRunnerFactory
import android.tools.flicker.legacy.FlickerBuilder
@@ -51,6 +52,7 @@
* apps are running before setup
* ```
*/
+@FlakyTest(bugId = 391734110)
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
index 40b685c..4972fa9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/ShellTestCase.java
@@ -23,6 +23,9 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableContext;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -31,6 +34,8 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
import org.mockito.MockitoAnnotations;
/**
@@ -38,6 +43,16 @@
*/
public abstract class ShellTestCase {
+ @ClassRule
+ public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
+
protected TestableContext mContext;
private PackageManager mPm;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index bba9418..94dc774 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -41,7 +41,6 @@
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.window.TransitionInfo;
@@ -55,7 +54,6 @@
import com.google.testing.junit.testparameterinjector.TestParameterInjector;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -73,9 +71,6 @@
@RunWith(TestParameterInjector.class)
public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnimationTestBase {
- @Rule
- public SetFlagsRule mRule = new SetFlagsRule();
-
@Before
public void setup() {
super.setUp();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index 39d5507..56948d4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -34,7 +34,6 @@
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -46,7 +45,6 @@
import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -64,9 +62,6 @@
private static final Rect EMBEDDED_LEFT_BOUNDS = new Rect(0, 0, 500, 500);
private static final Rect EMBEDDED_RIGHT_BOUNDS = new Rect(500, 0, 1000, 500);
- @Rule
- public SetFlagsRule mRule = new SetFlagsRule();
-
@Before
public void setup() {
super.setUp();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index bbdb90f..e63db9a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -60,7 +60,6 @@
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.IRemoteAnimationRunner;
@@ -91,7 +90,6 @@
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -152,9 +150,6 @@
private BackAnimationController.BackTransitionHandler mBackTransitionHandler;
- @Rule
- public SetFlagsRule mSetflagsRule = new SetFlagsRule();
-
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index 6d7a18d..2ef6c55 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -32,6 +32,8 @@
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.wm.shell.ShellTestCase;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -42,7 +44,7 @@
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
-public class BackProgressAnimatorTest {
+public class BackProgressAnimatorTest extends ShellTestCase {
private BackProgressAnimator mProgressAnimator;
private BackEvent mReceivedBackEvent;
private float mTargetProgress = 0.5f;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
index f8eb50b..622e4cb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubblesTransitionObserverTest.java
@@ -38,6 +38,7 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.transition.TransitionInfoBuilder;
import org.junit.Before;
@@ -49,7 +50,7 @@
* Tests of {@link BubblesTransitionObserver}.
*/
@SmallTest
-public class BubblesTransitionObserverTest {
+public class BubblesTransitionObserverTest extends ShellTestCase {
@Mock
private BubbleController mBubbleController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
index f8ee300..3323740 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DevicePostureControllerTest.java
@@ -29,6 +29,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
@@ -41,7 +42,7 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-public class DevicePostureControllerTest {
+public class DevicePostureControllerTest extends ShellTestCase {
@Mock
private Context mContext;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 6f3a3ec..ee9d177 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -39,8 +39,6 @@
import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.IWindowManager;
import android.view.InsetsSource;
import android.view.InsetsSourceControl;
@@ -55,7 +53,6 @@
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -70,9 +67,6 @@
*/
@SmallTest
public class DisplayImeControllerTest extends ShellTestCase {
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
-
@Mock
private SurfaceControl.Transaction mT;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
index 5a49d01..979cee9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIShellTestCase.java
@@ -16,24 +16,10 @@
package com.android.wm.shell.compatui;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
-import android.platform.test.flag.junit.SetFlagsRule;
-
import com.android.wm.shell.ShellTestCase;
-import org.junit.Rule;
-
/**
* Base class for CompatUI tests.
*/
public class CompatUIShellTestCase extends ShellTestCase {
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule =
- DeviceFlagsValueProvider.createCheckFlagsRule();
-
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 61b6d80..010474e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -38,6 +38,7 @@
import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.testing.AndroidTestingRunner;
import android.util.Pair;
@@ -394,8 +395,8 @@
@Test
@RequiresFlagsDisabled(FLAG_APP_COMPAT_UI_FRAMEWORK)
+ @EnableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON)
public void testShouldShowSizeCompatRestartButton() {
- mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_HIDE_SCM_BUTTON);
doReturn(85).when(mCompatUIConfiguration).getHideSizeCompatRestartButtonTolerance();
mWindowManager = new CompatUIWindowManager(mContext, mTaskInfo, mSyncTransactionQueue,
mCallback, mTaskListener, mDisplayLayout, new CompatUIHintsState(),
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
index 319122d..d3a2c9a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/impl/DefaultCompatUIRepositoryTest.kt
@@ -18,7 +18,6 @@
import android.graphics.Point
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.testing.AndroidTestingRunner
import android.view.View
import androidx.test.filters.SmallTest
@@ -29,7 +28,6 @@
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -45,9 +43,6 @@
lateinit var repository: CompatUIRepository
- @get:Rule
- val mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
-
@Before
fun setUp() {
repository = DefaultCompatUIRepository()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
index 78bb721..008c499 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/letterbox/LetterboxTransitionObserverTest.kt
@@ -20,7 +20,6 @@
import android.graphics.Rect
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CLOSE
@@ -36,14 +35,12 @@
import com.android.wm.shell.util.TransitionObserverInputBuilder
import com.android.wm.shell.util.executeTransitionObserverTest
import java.util.function.Consumer
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.times
import org.mockito.kotlin.verify
/**
@@ -56,9 +53,6 @@
@SmallTest
class LetterboxTransitionObserverTest : ShellTestCase() {
- @get:Rule
- val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
@Test
@DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING)
fun `when initialized and flag disabled the observer is not registered`() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
index 957fdf99..09ffd94 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt
@@ -25,7 +25,6 @@
import android.os.Binder
import android.os.UserManager
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.window.WindowContainerTransaction
@@ -62,7 +61,6 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -89,8 +87,6 @@
@ExperimentalCoroutinesApi
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE, FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE)
class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
@Mock lateinit var transitions: Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
index fae7363..0d5741f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt
@@ -23,7 +23,6 @@
import android.content.ContentResolver
import android.os.Binder
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.provider.Settings
import android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS
import android.testing.AndroidTestingRunner
@@ -51,7 +50,6 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.isNull
@@ -74,9 +72,6 @@
@SmallTest
@RunWith(AndroidTestingRunner::class)
class DesktopDisplayEventHandlerTest : ShellTestCase() {
-
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var transitions: Transitions
@Mock lateinit var displayController: DisplayController
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
index 47d133b..006c3ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopImmersiveControllerTest.kt
@@ -23,7 +23,6 @@
import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display.DEFAULT_DISPLAY
@@ -73,7 +72,6 @@
@RunWith(AndroidTestingRunner::class)
class DesktopImmersiveControllerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
@JvmField @Rule val animatorTestRule = AnimatorTestRule(this)
@Mock private lateinit var mockTransitions: Transitions
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index f61ea4a..f48bc99 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -27,7 +27,6 @@
import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
@@ -57,7 +56,6 @@
import org.junit.Assert.assertNull
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.ArgumentMatchers.anyInt
@@ -82,8 +80,6 @@
@RunWith(AndroidTestingRunner::class)
class DesktopMixedTransitionHandlerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
@Mock lateinit var transitions: Transitions
@Mock lateinit var userRepositories: DesktopUserRepositories
@Mock lateinit var freeformTaskTransitionHandler: FreeformTaskTransitionHandler
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
index bddc062..8a5acfa 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeEventLoggerTest.kt
@@ -19,7 +19,6 @@
import android.app.ActivityManager.RunningTaskInfo
import android.graphics.Rect
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import com.android.dx.mockito.inline.extended.ExtendedMockito.clearInvocations
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
import com.android.dx.mockito.inline.extended.ExtendedMockito.staticMockMarker
@@ -65,15 +64,13 @@
val displayLayout = mock<DisplayLayout>()
@JvmField
- @Rule(order = 0)
+ @Rule()
val extendedMockitoRule =
ExtendedMockitoRule.Builder(this)
.mockStatic(FrameworkStatsLog::class.java)
.mockStatic(EventLogTags::class.java)
.build()!!
- @JvmField @Rule(order = 1) val setFlagsRule = SetFlagsRule()
-
@Before
fun setUp() {
doReturn(displayLayout).whenever(displayController).getDisplayLayout(anyInt())
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
index 016e040..470c110 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt
@@ -24,7 +24,6 @@
import android.hardware.input.InputManager.KeyGestureEventHandler
import android.hardware.input.KeyGestureEvent
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.KeyEvent
@@ -64,7 +63,6 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
@@ -87,8 +85,6 @@
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
class DesktopModeKeyGestureHandlerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
private val rootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>()
private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
private val focusTransitionObserver = mock<FocusTransitionObserver>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
index f5c93ee..90f342f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopRepositoryTest.kt
@@ -20,7 +20,6 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
-import android.platform.test.flag.junit.SetFlagsRule
import android.util.ArraySet
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.INVALID_DISPLAY
@@ -47,7 +46,6 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -71,8 +69,6 @@
@ExperimentalCoroutinesApi
class DesktopRepositoryTest(flags: FlagsParameterization) : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule(flags)
-
private lateinit var repo: DesktopRepository
private lateinit var shellInit: ShellInit
private lateinit var datastoreScope: CoroutineScope
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
index 12c7ff6..50590f0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTaskChangeListenerTest.kt
@@ -18,7 +18,6 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
@@ -26,7 +25,6 @@
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFullscreenTask
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
@@ -44,8 +42,6 @@
@RunWith(AndroidTestingRunner::class)
class DesktopTaskChangeListenerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
private lateinit var desktopTaskChangeListener: DesktopTaskChangeListener
private val desktopUserRepositories = mock<DesktopUserRepositories>()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index a139f16..e7fe57d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -49,7 +49,6 @@
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
-import android.platform.test.flag.junit.SetFlagsRule
import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.Gravity
@@ -165,7 +164,6 @@
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -201,8 +199,6 @@
@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule(flags)
-
@Mock lateinit var testExecutor: ShellExecutor
@Mock lateinit var shellCommandHandler: ShellCommandHandler
@Mock lateinit var shellController: ShellController
@@ -538,6 +534,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
markTaskHidden(task1)
@@ -726,6 +723,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
markTaskVisible(task1)
@@ -764,7 +762,8 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() {
+ fun showDesktopApps_someAppsInvisible_desktopWallpaperEnabled_reordersOnlyFreeformTasks() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task1 = setUpFreeformTask()
val task2 = setUpFreeformTask()
markTaskHidden(task1)
@@ -781,6 +780,24 @@
wct.assertReorderAt(index = 2, task2)
}
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_someAppsInvisible_desktopWallpaperEnabled_reordersAll() {
+ val task1 = setUpFreeformTask()
+ val task2 = setUpFreeformTask()
+ markTaskHidden(task1)
+ markTaskVisible(task2)
+
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ assertThat(wct.hierarchyOps).hasSize(3)
+ // Expect order to be from bottom: wallpaper intent, task1, task2
+ wct.assertReorderAt(index = 0, wallpaperToken)
+ wct.assertReorderAt(index = 1, task1)
+ wct.assertReorderAt(index = 2, task2)
+ }
+
@Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() {
@@ -796,7 +813,9 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
- fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() {
+ fun showDesktopApps_noActiveTasks_desktopWallpaperEnabled_addsDesktopWallpaper() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
+
controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
val wct =
@@ -805,6 +824,16 @@
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
+ fun showDesktopApps_noActiveTasks_desktopWallpaperEnabled_reordersDesktopWallpaper() {
+ controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition()))
+
+ val wct =
+ getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java)
+ wct.assertReorderAt(index = 0, wallpaperToken)
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() {
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
@@ -828,6 +857,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
taskRepository.addDesk(displayId = SECOND_DISPLAY, deskId = SECOND_DISPLAY)
val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY)
val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY)
@@ -872,6 +902,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun showDesktopApps_desktopWallpaperEnabled_dontReorderMinimizedTask() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val homeTask = setUpHomeTask()
val freeformTask = setUpFreeformTask()
val minimizedTask = setUpFreeformTask()
@@ -1337,6 +1368,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveTaskToDesktop_desktopWallpaperEnabled_nonRunningTask_launchesInFreeform() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task = createTaskInfo(1)
whenever(shellTaskOrganizer.getRunningTaskInfo(anyInt())).thenReturn(null)
whenever(recentTasksController.findTaskInBackground(anyInt())).thenReturn(task)
@@ -1484,6 +1516,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveRunningTaskToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTask = setUpFreeformTask()
val fullscreenTask = setUpFullscreenTask()
markTaskHidden(freeformTask)
@@ -1586,6 +1619,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun moveRunningTaskToDesktop_desktopWallpaperEnabled_bringsTasksOverLimit_dontShowBackTask() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTasks = (1..MAX_TASK_LIMIT).map { _ -> setUpFreeformTask() }
val newTask = setUpFullscreenTask()
val homeTask = setUpHomeTask()
@@ -2606,6 +2640,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_fullscreenTask_noTasks_enforceDesktop_freeformDisplay_returnFreeformWCT() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
whenever(DesktopModeStatus.enterDesktopByDefaultOnFreeformDisplay(context)).thenReturn(true)
val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!!
tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
@@ -2737,6 +2772,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_desktopWallpaperEnabled_freeformNotVisible_reorderedToTop() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val freeformTask1 = setUpFreeformTask()
val freeformTask2 = createFreeformTask()
@@ -2771,7 +2807,9 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_desktopWallpaperEnabled_noOtherTasks_reorderedToTop() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val task = createFreeformTask()
+
val result = controller.handleRequest(Binder(), createTransition(task))
assertNotNull(result, "Should handle request")
@@ -2799,6 +2837,7 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
fun handleRequest_freeformTask_dskWallpaperEnabled_freeformOnOtherDisplayOnly_reorderedToTop() {
+ whenever(desktopWallpaperActivityTokenProvider.getToken()).thenReturn(null)
val taskDefaultDisplay = createFreeformTask(displayId = DEFAULT_DISPLAY)
// Second display task
createFreeformTask(displayId = SECOND_DISPLAY)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 554b09f..d33209d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -24,7 +24,6 @@
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.SurfaceControl
@@ -66,7 +65,6 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -88,8 +86,6 @@
@ExperimentalCoroutinesApi
class DesktopTasksLimiterTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
@Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
@Mock lateinit var transitions: Transitions
@Mock lateinit var interactionJankMonitor: InteractionJankMonitor
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index ca1e3ed..091159c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -25,7 +25,6 @@
import android.os.Binder
import android.os.IBinder
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.view.Display.DEFAULT_DISPLAY
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_CLOSE
@@ -77,9 +76,6 @@
* Build/Install/Run: atest WMShellUnitTests:DesktopTasksTransitionObserverTest
*/
class DesktopTasksTransitionObserverTest {
-
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
@JvmField
@Rule
val extendedMockitoRule =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
index b9e307fa5..83e4872 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopUserRepositoriesTest.kt
@@ -21,7 +21,6 @@
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
@@ -44,7 +43,6 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy
@@ -56,8 +54,6 @@
@RunWith(AndroidTestingRunner::class)
@ExperimentalCoroutinesApi
class DesktopUserRepositoriesTest : ShellTestCase() {
- @get:Rule val setFlagsRule = SetFlagsRule()
-
private lateinit var userRepositories: DesktopUserRepositories
private lateinit var shellInit: ShellInit
private lateinit var datastoreScope: CoroutineScope
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
index 1160a92..86e8142 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/education/AppHandleEducationControllerTest.kt
@@ -19,7 +19,6 @@
import android.os.SystemProperties
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableContext
import androidx.test.filters.SmallTest
@@ -75,7 +74,6 @@
.mockStatic(DesktopModeStatus::class.java)
.mockStatic(SystemProperties::class.java)
.build()!!
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
private lateinit var educationController: AppHandleEducationController
private lateinit var testableContext: TestableContext
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
index 9a8f264..dd9e6ca 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt
@@ -19,7 +19,6 @@
import android.os.UserManager
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import androidx.test.filters.SmallTest
@@ -42,7 +41,6 @@
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.spy
@@ -54,8 +52,6 @@
@ExperimentalCoroutinesApi
class DesktopRepositoryInitializerTest : ShellTestCase() {
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
-
private lateinit var repositoryInitializer: DesktopRepositoryInitializer
private lateinit var shellInit: ShellInit
private lateinit var datastoreScope: CoroutineScope
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
index 4174bbd..9509aaf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskListenerTests.java
@@ -35,7 +35,6 @@
import android.app.ActivityManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -56,7 +55,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -72,9 +70,6 @@
@RunWith(AndroidJUnit4.class)
public final class FreeformTaskListenerTests extends ShellTestCase {
- @Rule
- public final SetFlagsRule setFlagsRule = new SetFlagsRule();
-
@Mock
private ShellTaskOrganizer mTaskOrganizer;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
index 5aed461..bc91845 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserverTest.java
@@ -34,7 +34,6 @@
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import android.window.IWindowContainerToken;
import android.window.TransitionInfo;
@@ -43,6 +42,7 @@
import androidx.test.filters.SmallTest;
import com.android.window.flags.Flags;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
@@ -60,9 +60,8 @@
/** Tests for {@link FreeformTaskTransitionObserver}. */
@SmallTest
-public class FreeformTaskTransitionObserverTest {
+public class FreeformTaskTransitionObserverTest extends ShellTestCase {
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Mock private ShellInit mShellInit;
@Mock private Transitions mTransitions;
@Mock private DesktopImmersiveController mDesktopImmersiveController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 836f4c2..2668823 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -37,7 +37,6 @@
import android.graphics.Rect;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Rational;
@@ -70,8 +69,6 @@
import com.android.wm.shell.splitscreen.SplitScreenController;
import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentMatchers;
@@ -88,11 +85,6 @@
@TestableLooper.RunWithLooper
@DisableFlags(Flags.FLAG_ENABLE_PIP2)
public class PipTaskOrganizerTest extends ShellTestCase {
- @ClassRule
- public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
- @Rule
- public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
-
private PipTaskOrganizer mPipTaskOrganizer;
@Mock private DisplayController mMockDisplayController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index 5ef934c..13fce2a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -43,7 +43,6 @@
import android.os.Handler;
import android.os.RemoteException;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -77,8 +76,6 @@
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -95,9 +92,6 @@
@TestableLooper.RunWithLooper
@DisableFlags(Flags.FLAG_ENABLE_PIP2)
public class PipControllerTest extends ShellTestCase {
- @ClassRule public static final SetFlagsRule.ClassRule mClassRule = new SetFlagsRule.ClassRule();
- @Rule public final SetFlagsRule mSetFlagsRule = mClassRule.createSetFlagsRule();
-
private PipController mPipController;
private ShellInit mShellInit;
private ShellController mShellController;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
index b11715b..273cb27 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchHandlerTest.java
@@ -25,7 +25,6 @@
import android.graphics.Rect;
import android.platform.test.annotations.DisableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.Size;
@@ -49,7 +48,6 @@
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -68,9 +66,6 @@
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class PipTouchHandlerTest extends ShellTestCase {
- @Rule
- public SetFlagsRule setFlagsRule = new SetFlagsRule();
-
private static final int INSET = 10;
private static final int PIP_LENGTH = 100;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index 542289d..5028479 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -63,7 +63,6 @@
import android.os.Bundle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -89,7 +88,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -129,9 +127,6 @@
@Mock
private DesktopRepository mDesktopRepository;
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
private RecentTasksController mRecentTasksControllerReal;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
index ab43119..b50af74 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentsTransitionHandlerTest.java
@@ -47,7 +47,6 @@
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -77,7 +76,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -115,9 +113,6 @@
@Mock private DesktopRepository mDesktopRepository;
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
private RecentTasksController mRecentTasksControllerReal;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
index 9919462..769407b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt
@@ -26,7 +26,6 @@
import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.SurfaceControl
import android.view.WindowManager
@@ -42,6 +41,7 @@
import androidx.test.filters.SmallTest
import com.android.window.flags.Flags
import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.TestSyncExecutor
import com.android.wm.shell.common.ShellExecutor
@@ -53,7 +53,6 @@
import com.google.common.truth.Truth.assertThat
import dagger.Lazy
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -71,9 +70,7 @@
*/
@SmallTest
@RunWith(AndroidTestingRunner::class)
-class TaskStackTransitionObserverTest {
-
- @JvmField @Rule val setFlagsRule = SetFlagsRule()
+class TaskStackTransitionObserverTest : ShellTestCase() {
@Mock private lateinit var shellInit: ShellInit
@Mock private lateinit var shellTaskOrganizerLazy: Lazy<ShellTaskOrganizer>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
new file mode 100644
index 0000000..4dac99b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatusTest.kt
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2025 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.wm.shell.shared.desktopmode
+
+import android.content.Context
+import android.content.res.Resources
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.provider.Settings
+import android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES
+import android.window.DesktopModeFlags
+import androidx.test.filters.SmallTest
+import com.android.internal.R
+import com.android.window.flags.Flags
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@Presubmit
+@EnableFlags(Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+class DesktopModeStatusTest : ShellTestCase() {
+ @get:Rule
+ val mSetFlagsRule = SetFlagsRule()
+
+ private val mockContext = mock<Context>()
+ private val mockResources = mock<Resources>()
+
+ @Before
+ fun setUp() {
+ doReturn(mockResources).whenever(mockContext).getResources()
+ doReturn(false).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+ doReturn(false).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+ doReturn(context.contentResolver).whenever(mockContext).contentResolver
+ resetDesktopModeFlagsCache()
+ resetEnforceDeviceRestriction()
+ resetFlagOverride()
+ }
+
+ @After
+ fun tearDown() {
+ resetDesktopModeFlagsCache()
+ resetEnforceDeviceRestriction()
+ resetFlagOverride()
+ }
+
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+ )
+ @Test
+ fun canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() {
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+ }
+
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+ )
+ @Test
+ fun canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceRestrictions_returnsFalse() {
+ doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+ doReturn(true).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+ disableEnforceDeviceRestriction()
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+ }
+
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+ )
+ @Test
+ fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() {
+ doReturn(true).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+ }
+
+ @DisableFlags(
+ Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION
+ )
+ @Test
+ fun canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+ doReturn(true).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+ setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON)
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ fun canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() {
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() {
+ doReturn(true).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isFalse()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ fun canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() {
+ doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ fun canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue() {
+ disableEnforceDeviceRestriction()
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ fun canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+ doReturn(true).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+ setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON)
+
+ assertThat(DesktopModeStatus.canEnterDesktopMode(mockContext)).isTrue()
+ }
+
+ @Test
+ fun isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() {
+ doReturn(true).whenever(mockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported))
+
+ assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue()
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @Test
+ fun isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() {
+ assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse()
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @Test
+ fun isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() {
+ assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isFalse()
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @Test
+ fun isDeviceEligibleForDesktopMode_supportFlagOn_configDevOptModeOn_returnsTrue() {
+ doReturn(true).whenever(mockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ )
+
+ assertThat(DesktopModeStatus.isDeviceEligibleForDesktopMode(mockContext)).isTrue()
+ }
+
+ private fun resetEnforceDeviceRestriction() {
+ setEnforceDeviceRestriction(true)
+ }
+
+ private fun disableEnforceDeviceRestriction() {
+ setEnforceDeviceRestriction(false)
+ }
+
+ private fun setEnforceDeviceRestriction(value: Boolean) {
+ val field = DesktopModeStatus::class.java.getDeclaredField("ENFORCE_DEVICE_RESTRICTIONS")
+ field.isAccessible = true
+ field.setBoolean(null, value)
+ }
+
+ private fun resetDesktopModeFlagsCache() {
+ val cachedToggleOverride =
+ DesktopModeFlags::class.java.getDeclaredField("sCachedToggleOverride")
+ cachedToggleOverride.isAccessible = true
+ cachedToggleOverride.set(null, null)
+ }
+
+ private fun resetFlagOverride() {
+ Settings.Global.putString(
+ context.contentResolver,
+ DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null
+ )
+ }
+
+ private fun setFlagOverride(override: DesktopModeFlags.ToggleOverride) {
+ Settings.Global.putInt(
+ context.contentResolver,
+ DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.setting
+ )
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
index 6ac34d7..2d454a5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTest.java
@@ -51,7 +51,6 @@
import android.graphics.Region;
import android.os.Looper;
import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.view.SurfaceHolder;
@@ -72,7 +71,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -96,9 +94,6 @@
return FlagsParameterization.allCombinationsOf(Flags.FLAG_TASK_VIEW_REPOSITORY);
}
- @Rule
- public final SetFlagsRule mSetFlagsRule;
-
@Mock
TaskView.Listener mViewListener;
@Mock
@@ -127,9 +122,7 @@
TaskViewTransitions mTaskViewTransitions;
TaskViewTaskController mTaskViewTaskController;
- public TaskViewTest(FlagsParameterization flags) {
- mSetFlagsRule = new SetFlagsRule(flags);
- }
+ public TaskViewTest(FlagsParameterization flags) {}
@Before
public void setUp() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
index 326f11e..3a455ba 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/taskview/TaskViewTransitionsTest.java
@@ -34,7 +34,6 @@
import android.graphics.Rect;
import android.os.IBinder;
import android.platform.test.flag.junit.FlagsParameterization;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -50,7 +49,6 @@
import com.android.wm.shell.transition.Transitions;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -74,9 +72,6 @@
Flags.FLAG_ENABLE_TASK_VIEW_CONTROLLER_CLEANUP);
}
- @Rule
- public final SetFlagsRule mSetFlagsRule;
-
@Mock
Transitions mTransitions;
@Mock
@@ -95,9 +90,7 @@
TaskViewRepository mTaskViewRepository;
TaskViewTransitions mTaskViewTransitions;
- public TaskViewTransitionsTest(FlagsParameterization flags) {
- mSetFlagsRule = new SetFlagsRule(flags);
- }
+ public TaskViewTransitionsTest(FlagsParameterization flags) {}
@Before
public void setUp() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
index 74c2f0e..96e4f49 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/FocusTransitionObserverTest.java
@@ -35,8 +35,6 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.os.RemoteException;
import android.platform.test.annotations.RequiresFlagsEnabled;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
@@ -50,7 +48,6 @@
import com.android.wm.shell.shared.FocusTransitionListener;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,8 +64,6 @@
static final int SECONDARY_DISPLAY_ID = 1;
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private FocusTransitionListener mListener;
private final TestShellExecutor mShellExecutor = new TestShellExecutor();
private FocusTransitionObserver mFocusTransitionObserver;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
index 3e53ee5..6f28e656 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java
@@ -39,8 +39,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.platform.test.flag.junit.CheckFlagsRule;
-import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionInfo.TransitionMode;
@@ -60,7 +58,6 @@
import com.android.wm.shell.sysui.ShellInit;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,9 +70,6 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class HomeTransitionObserverTest extends ShellTestCase {
-
- @Rule
- public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private final ShellTaskOrganizer mOrganizer = mock(ShellTaskOrganizer.class);
private final TransactionPool mTransactionPool = mock(TransactionPool.class);
private final Context mContext =
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 0a19be4..efbc533 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -74,7 +74,8 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.platform.test.flag.junit.SetFlagsRule;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.util.ArraySet;
import android.util.Pair;
import android.view.Surface;
@@ -117,7 +118,6 @@
import com.android.wm.shell.util.StubTransaction;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
@@ -145,9 +145,6 @@
private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler();
private final Handler mMainHandler = new Handler(Looper.getMainLooper());
- @Rule
- public final SetFlagsRule setFlagsRule = new SetFlagsRule();
-
@Before
public void setUp() {
doAnswer(invocation -> new Binder())
@@ -553,7 +550,8 @@
}
@Test
- public void testRegisteredRemoteTransitionTakeover() {
+ @DisableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
+ public void testRegisteredRemoteTransitionTakeover_flagDisabled() {
Transitions transitions = createTestTransitions();
transitions.replaceDefaultHandlerForTest(mDefaultHandler);
@@ -608,7 +606,6 @@
mMainExecutor.flushAll();
// Takeover shouldn't happen when the flag is disabled.
- setFlagsRule.disableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED);
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
@@ -621,12 +618,69 @@
mDefaultHandler.finishAll();
mMainExecutor.flushAll();
verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any());
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED)
+ public void testRegisteredRemoteTransitionTakeover_flagEnabled() {
+ Transitions transitions = createTestTransitions();
+ transitions.replaceDefaultHandlerForTest(mDefaultHandler);
+
+ IRemoteTransition testRemote = new RemoteTransitionStub() {
+ @Override
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishCallback) throws RemoteException {
+ final Transitions.TransitionHandler takeoverHandler =
+ transitions.getHandlerForTakeover(token, info);
+
+ if (takeoverHandler == null) {
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ return;
+ }
+
+ takeoverHandler.takeOverAnimation(token, info, new SurfaceControl.Transaction(),
+ wct -> {
+ try {
+ finishCallback.onTransitionFinished(wct, null /* sct */);
+ } catch (RemoteException e) {
+ // Fail
+ }
+ }, new WindowAnimationState[info.getChanges().size()]);
+ }
+ };
+ final boolean[] takeoverRemoteCalled = new boolean[]{false};
+ IRemoteTransition testTakeoverRemote = new RemoteTransitionStub() {
+ @Override
+ public void startAnimation(IBinder token, TransitionInfo info,
+ SurfaceControl.Transaction t,
+ IRemoteTransitionFinishedCallback finishCallback) {}
+
+ @Override
+ public void takeOverAnimation(IBinder transition, TransitionInfo info,
+ SurfaceControl.Transaction startTransaction,
+ IRemoteTransitionFinishedCallback finishCallback, WindowAnimationState[] states)
+ throws RemoteException {
+ takeoverRemoteCalled[0] = true;
+ finishCallback.onTransitionFinished(null /* wct */, null /* sct */);
+ }
+ };
+
+ TransitionFilter filter = new TransitionFilter();
+ filter.mRequirements =
+ new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
+ filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
+
+ transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test"));
+ transitions.registerRemoteForTakeover(
+ filter, new RemoteTransition(testTakeoverRemote, "Test"));
+ mMainExecutor.flushAll();
// Takeover should happen when the flag is enabled.
- setFlagsRule.enableFlags(Flags.FLAG_RETURN_ANIMATION_FRAMEWORK_LONG_LIVED);
+ IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
- info = new TransitionInfoBuilder(TRANSIT_OPEN)
+ TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
transitions.onTransitionReady(transitToken, info, new StubTransaction(),
new StubTransaction());
@@ -634,7 +688,7 @@
assertTrue(takeoverRemoteCalled[0]);
mDefaultHandler.finishAll();
mMainExecutor.flushAll();
- verify(mOrganizer, times(2)).finishTransition(eq(transitToken), any());
+ verify(mOrganizer, times(1)).finishTransition(eq(transitToken), any());
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
index 71af97e..5ba2f18 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/unfold/UnfoldTransitionHandlerTest.java
@@ -43,6 +43,7 @@
import android.window.TransitionRequestInfo;
import android.window.WindowContainerTransaction;
+import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestSyncExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.shared.TransactionPool;
@@ -61,7 +62,7 @@
import java.util.List;
import java.util.concurrent.Executor;
-public class UnfoldTransitionHandlerTest {
+public class UnfoldTransitionHandlerTest extends ShellTestCase {
private UnfoldTransitionHandler mUnfoldTransitionHandler;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
index cf6c3a5e..257bbb5 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopHeaderManageWindowsMenuTest.kt
@@ -19,7 +19,6 @@
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.SurfaceControl
@@ -35,7 +34,6 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.mock
@@ -51,10 +49,6 @@
@RunWith(AndroidTestingRunner::class)
class DesktopHeaderManageWindowsMenuTest : ShellTestCase() {
- @JvmField
- @Rule
- val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
private lateinit var userRepositories: DesktopUserRepositories
private lateinit var menu: DesktopHeaderManageWindowsMenu
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
index c5c8274..541b19cf 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTestsBase.kt
@@ -25,9 +25,6 @@
import android.hardware.input.InputManager
import android.os.Handler
import android.os.UserHandle
-import android.platform.test.flag.junit.CheckFlagsRule
-import android.platform.test.flag.junit.DeviceFlagsValueProvider
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.TestableContext
import android.util.SparseArray
import android.view.Choreographer
@@ -84,7 +81,6 @@
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier
import com.android.wm.shell.windowdecor.viewholder.AppHeaderViewHolder
import org.junit.After
-import org.junit.Rule
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.kotlin.any
@@ -105,14 +101,6 @@
*/
@OptIn(kotlinx.coroutines.ExperimentalCoroutinesApi::class)
open class DesktopModeWindowDecorViewModelTestsBase : ShellTestCase() {
- @JvmField
- @Rule
- val setFlagsRule = SetFlagsRule()
-
- @JvmField
- @Rule
- val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
-
private val mockDesktopModeWindowDecorFactory = mock<DesktopModeWindowDecoration.Factory>()
protected val mockMainHandler = mock<Handler>()
protected val mockMainChoreographer = mock<Choreographer>()
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 76e1e80..18a780b 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
@@ -20,7 +20,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
import static android.view.WindowInsets.Type.captionBar;
@@ -69,7 +68,6 @@
import android.os.SystemProperties;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.testing.TestableContext;
import android.testing.TestableLooper;
@@ -129,7 +127,6 @@
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;
import org.mockito.ArgumentCaptor;
@@ -171,8 +168,6 @@
private static final boolean DEFAULT_HAS_GLOBAL_FOCUS = true;
private static final boolean DEFAULT_SHOULD_IGNORE_CORNER_RADIUS = false;
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
@Mock
private DisplayController mMockDisplayController;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
index a20a89c..ab9dbc9 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragPositioningCallbackUtilityTest.kt
@@ -23,7 +23,6 @@
import android.os.IBinder
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.view.Display
import android.window.WindowContainerToken
@@ -44,7 +43,6 @@
import junit.framework.Assert.assertTrue
import org.junit.After
import org.junit.Before
-import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
@@ -85,10 +83,6 @@
@Mock
private lateinit var mockResources: Resources
- @JvmField
- @Rule
- val setFlagsRule = SetFlagsRule()
-
private lateinit var mockitoSession: StaticMockitoSession
@Before
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
index 479f156..3389ec1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DragResizeWindowGeometryTests.java
@@ -30,7 +30,6 @@
import android.graphics.Region;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.Size;
@@ -41,7 +40,6 @@
import com.google.common.testing.EqualsTester;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -87,9 +85,6 @@
private static final Point BOTTOM_INSET_POINT = new Point(TASK_SIZE.getWidth() / 2,
TASK_SIZE.getHeight() - EDGE_RESIZE_HANDLE_INSET / 2);
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
/**
* Check that both groups of objects satisfy equals/hashcode within each group, and that each
* group is distinct from the next.
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
index f90988e..f984f6d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/HandleMenuTest.kt
@@ -26,7 +26,6 @@
import android.graphics.Rect
import android.graphics.drawable.BitmapDrawable
import android.platform.test.annotations.EnableFlags
-import android.platform.test.flag.junit.SetFlagsRule
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.Display
@@ -60,7 +59,6 @@
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.ArgumentMatchers.anyInt
@@ -81,10 +79,6 @@
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner::class)
class HandleMenuTest : ShellTestCase() {
- @JvmField
- @Rule
- val setFlagsRule: SetFlagsRule = SetFlagsRule()
-
@Mock
private lateinit var mockDesktopWindowDecoration: DesktopModeWindowDecoration
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index d969346..3a8dcd6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -18,7 +18,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
import static android.view.WindowInsets.Type.captionBar;
@@ -62,7 +61,6 @@
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Handler;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.AndroidTestingRunner;
import android.util.DisplayMetrics;
import android.view.AttachedSurfaceControl;
@@ -93,7 +91,6 @@
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -120,9 +117,6 @@
private static final int SHADOW_RADIUS = 10;
private static final int STATUS_BAR_INSET_SOURCE_ID = 0;
- @Rule
- public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
-
private final WindowDecoration.RelayoutResult<TestView> mRelayoutResult =
new WindowDecoration.RelayoutResult<>();
diff --git a/media/java/android/media/OWNERS b/media/java/android/media/OWNERS
index b6096a1..a600017 100644
--- a/media/java/android/media/OWNERS
+++ b/media/java/android/media/OWNERS
@@ -1,6 +1,7 @@
# Bug component: 1344
-fgoldfain@google.com
+pshehane@google.com
elaurent@google.com
+etalvala@google.com
lajos@google.com
jmtrivi@google.com
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index e558209..e4de3e4 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -341,6 +341,13 @@
public static final String PARAMETER_FILM_MODE = "film_mode";
/**
+ * Enable/disable black color auto stretch
+ *
+ * @hide
+ */
+ public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
+
+ /**
* Enable/disable blue color auto stretch
*
* <p>Type: BOOLEAN
@@ -457,6 +464,27 @@
* @hide
*
*/
+ public static final String PARAMETER_COLOR_TEMPERATURE_RED_GAIN =
+ "color_temperature_red_gain";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TEMPERATURE_GREEN_GAIN =
+ "color_temperature_green_gain";
+
+ /**
+ * @hide
+ *
+ */
+ public static final String PARAMETER_COLOR_TEMPERATURE_BLUE_GAIN =
+ "color_temperature_blue_gain";
+
+ /**
+ * @hide
+ *
+ */
public static final String PARAMETER_COLOR_TEMPERATURE_RED_OFFSET =
"color_temperature_red_offset";
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
index 292aa51..6d30f49 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespaces.java
@@ -43,6 +43,7 @@
"netd_native",
"network_security",
"on_device_personalization",
+ "testing",
"tethering",
"tethering_u_or_later_native",
"thread_network"
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index dace50f..bb0d5d7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -196,6 +196,18 @@
}
flag {
+ name: "notification_undo_guts_on_config_changed"
+ namespace: "systemui"
+ description: "Fixes a bug where a theme or font change while notification guts were open"
+ " (e.g. the snooze options or notification info) would show an empty notification by"
+ " closing the guts and undoing changes."
+ bug: "379267630"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "pss_app_selector_recents_split_screen"
namespace: "systemui"
description: "Allows recent apps selected for partial screenshare to be launched in split screen mode"
@@ -575,20 +587,6 @@
}
flag {
- name: "clock_reactive_variants"
- namespace: "systemui"
- description: "Add reactive variant fonts to some clocks"
- bug: "343495953"
-}
-
-flag {
- name: "lockscreen_custom_clocks"
- namespace: "systemui"
- description: "Enable lockscreen custom clocks"
- bug: "378486437"
-}
-
-flag {
name: "faster_unlock_transition"
namespace: "systemui"
description: "Faster wallpaper unlock transition"
@@ -1828,6 +1826,13 @@
}
flag {
+ name: "notification_row_transparency"
+ namespace: "systemui"
+ description: "Enables transparency on the Notification Shade."
+ bug: "392187268"
+}
+
+flag {
name: "shade_expands_on_status_bar_long_press"
namespace: "systemui"
description: "Expands the shade on long press of any status bar"
diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
index 0f6e6a7..f490968b 100644
--- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
+++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/scene/LockscreenSceneModule.kt
@@ -34,7 +34,6 @@
import dagger.Provides
import dagger.multibindings.IntoSet
import javax.inject.Provider
-import kotlinx.coroutines.ExperimentalCoroutinesApi
@Module(includes = [LockscreenSceneBlueprintModule::class])
interface LockscreenSceneModule {
@@ -43,7 +42,6 @@
companion object {
- @OptIn(ExperimentalCoroutinesApi::class)
@Provides
@SysUISingleton
@KeyguardRootView
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index 30dfa5b..31aebc2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -156,14 +156,8 @@
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
- val screensaverButtonSizeInt = screensaverButtonSize.roundToPx()
val screensaverButtonPlaceable =
- screensaverButtonMeasurable?.measure(
- Constraints.fixed(
- width = screensaverButtonSizeInt,
- height = screensaverButtonSizeInt,
- )
- )
+ screensaverButtonMeasurable?.measure(noMinConstraints)
val communalGridPlaceable =
communalGridMeasurable.measure(
@@ -181,12 +175,12 @@
screensaverButtonPlaceable?.place(
x =
constraints.maxWidth -
- screensaverButtonSizeInt -
- screensaverButtonPaddingInt,
+ screensaverButtonPaddingInt -
+ screensaverButtonPlaceable.width,
y =
constraints.maxHeight -
- screensaverButtonSizeInt -
- screensaverButtonPaddingInt,
+ screensaverButtonPaddingInt -
+ screensaverButtonPlaceable.height,
)
}
}
@@ -194,7 +188,6 @@
}
companion object {
- private val screensaverButtonSize: Dp = 64.dp
private val screensaverButtonPadding: Dp = 24.dp
// TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
index 9421596..13d551a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/CommunalToDreamButtonSection.kt
@@ -16,14 +16,37 @@
package com.android.systemui.communal.ui.compose.section
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.CornerSize
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.CornerRadius
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.RoundRect
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.geometry.toRect
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.graphics.Path
+import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.res.stringResource
-import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
import com.android.compose.PlatformIconButton
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.ui.compose.extensions.observeTaps
import com.android.systemui.communal.ui.viewmodel.CommunalToDreamButtonViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R
@@ -43,23 +66,111 @@
val viewModel =
rememberViewModel("CommunalToDreamButtonSection") { viewModelFactory.create() }
- val shouldShowDreamButtonOnHub by
- viewModel.shouldShowDreamButtonOnHub.collectAsStateWithLifecycle(false)
- if (!shouldShowDreamButtonOnHub) {
+ if (!viewModel.shouldShowDreamButtonOnHub) {
return
}
- PlatformIconButton(
- onClick = { viewModel.onShowDreamButtonTap() },
- iconResource = R.drawable.ic_screensaver_auto,
- contentDescription =
- stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
- colors =
- IconButtonDefaults.filledIconButtonColors(
- contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
- containerColor = MaterialTheme.colorScheme.primaryContainer,
- ),
+ if (viewModel.shouldShowTooltip) {
+ Column(
+ modifier =
+ Modifier.widthIn(max = tooltipMaxWidth).pointerInput(Unit) {
+ observeTaps { viewModel.setDreamButtonTooltipDismissed() }
+ }
+ ) {
+ Tooltip(
+ pointerOffsetDp = buttonSize.div(2),
+ text = stringResource(R.string.glanceable_hub_to_dream_button_tooltip),
+ )
+ GoToDreamButton(
+ modifier = Modifier.width(buttonSize).height(buttonSize).align(Alignment.End)
+ ) {
+ viewModel.onShowDreamButtonTap()
+ }
+ }
+ } else {
+ GoToDreamButton(modifier = Modifier.width(buttonSize).height(buttonSize)) {
+ viewModel.onShowDreamButtonTap()
+ }
+ }
+ }
+
+ companion object {
+ private val buttonSize = 64.dp
+ private val tooltipMaxWidth = 350.dp
+ }
+}
+
+@Composable
+private fun GoToDreamButton(modifier: Modifier, onClick: () -> Unit) {
+ PlatformIconButton(
+ modifier = modifier,
+ onClick = onClick,
+ iconResource = R.drawable.ic_screensaver_auto,
+ contentDescription = stringResource(R.string.accessibility_glanceable_hub_to_dream_button),
+ colors =
+ IconButtonDefaults.filledIconButtonColors(
+ contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ containerColor = MaterialTheme.colorScheme.primaryContainer,
+ ),
+ )
+}
+
+@Composable
+private fun Tooltip(pointerOffsetDp: Dp, text: String) {
+ Surface(
+ color = MaterialTheme.colorScheme.surface,
+ shape = TooltipShape(pointerSizeDp = 12.dp, pointerOffsetDp = pointerOffsetDp),
+ ) {
+ Text(
+ modifier = Modifier.padding(start = 32.dp, top = 16.dp, end = 32.dp, bottom = 32.dp),
+ color = MaterialTheme.colorScheme.onSurface,
+ text = text,
)
}
+
+ Spacer(modifier = Modifier.height(4.dp))
+}
+
+private class TooltipShape(private val pointerSizeDp: Dp, private val pointerOffsetDp: Dp) : Shape {
+
+ override fun createOutline(
+ size: Size,
+ layoutDirection: LayoutDirection,
+ density: Density,
+ ): Outline {
+
+ val pointerSizePx = with(density) { pointerSizeDp.toPx() }
+ val pointerOffsetPx = with(density) { pointerOffsetDp.toPx() }
+ val cornerRadius = CornerRadius(CornerSize(16.dp).toPx(size, density))
+ val bubbleSize = size.copy(height = size.height - pointerSizePx)
+
+ val path =
+ Path().apply {
+ addRoundRect(
+ RoundRect(
+ rect = bubbleSize.toRect(),
+ topLeft = cornerRadius,
+ topRight = cornerRadius,
+ bottomRight = cornerRadius,
+ bottomLeft = cornerRadius,
+ )
+ )
+ addPath(
+ Path().apply {
+ moveTo(0f, 0f)
+ lineTo(pointerSizePx / 2f, pointerSizePx)
+ lineTo(pointerSizePx, 0f)
+ close()
+ },
+ offset =
+ Offset(
+ x = bubbleSize.width - pointerOffsetPx - pointerSizePx / 2f,
+ y = bubbleSize.height,
+ ),
+ )
+ }
+
+ return Outline.Generic(path)
+ }
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
index d022150..500527f 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -57,9 +57,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-@ExperimentalCoroutinesApi
@Composable
fun AlternateBouncer(
alternateBouncerDependencies: AlternateBouncerDependencies,
@@ -127,7 +125,6 @@
}
}
-@ExperimentalCoroutinesApi
@Composable
private fun StatusMessage(
viewModel: AlternateBouncerMessageAreaViewModel,
@@ -156,7 +153,6 @@
}
}
-@ExperimentalCoroutinesApi
@Composable
private fun DeviceEntryIcon(
viewModel: AlternateBouncerUdfpsIconViewModel,
@@ -179,7 +175,6 @@
}
/** TODO (b/353955910): Validate accessibility CUJs */
-@ExperimentalCoroutinesApi
@Composable
private fun UdfpsA11yOverlay(
viewModel: AlternateBouncerUdfpsAccessibilityOverlayViewModel,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 478970f..d341702 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -45,7 +45,6 @@
import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.res.R
-import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod
import java.util.Optional
import javax.inject.Inject
import kotlin.math.roundToInt
@@ -130,9 +129,7 @@
if (!isShadeLayoutWide && !isBypassEnabled) {
Box(modifier = Modifier.weight(weight = 1f)) {
Column(Modifier.align(alignment = Alignment.TopStart)) {
- if (PromotedNotificationUiAod.isEnabled) {
- AodPromotedNotification()
- }
+ AodPromotedNotificationArea()
AodNotificationIcons(
modifier = Modifier.padding(start = aodIconPadding)
)
@@ -145,9 +142,7 @@
}
} else {
Column {
- if (PromotedNotificationUiAod.isEnabled) {
- AodPromotedNotification()
- }
+ AodPromotedNotificationArea()
AodNotificationIcons(
modifier = Modifier.padding(start = aodIconPadding)
)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
index b66690c..abf7fdc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/NotificationSection.kt
@@ -54,6 +54,7 @@
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.StatusBarIconViewBindingFailureTracker
import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel
import com.android.systemui.statusbar.notification.promoted.AODPromotedNotification
+import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUiAod
import com.android.systemui.statusbar.notification.promoted.ui.viewmodel.AODPromotedNotificationViewModel
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
import com.android.systemui.statusbar.notification.stack.ui.view.NotificationScrollView
@@ -110,8 +111,23 @@
}
@Composable
- fun AodPromotedNotification() {
- AODPromotedNotification(aodPromotedNotificationViewModelFactory)
+ fun AodPromotedNotificationArea(modifier: Modifier = Modifier) {
+ if (!PromotedNotificationUiAod.isEnabled) {
+ return
+ }
+
+ val isVisible by
+ keyguardRootViewModel.isAodPromotedNotifVisible.collectAsStateWithLifecycle()
+ val burnIn = rememberBurnIn(clockInteractor)
+
+ AnimatedVisibility(
+ visible = isVisible,
+ enter = fadeIn(),
+ exit = fadeOut(),
+ modifier = modifier.burnInAware(aodBurnInViewModel, burnIn.parameters),
+ ) {
+ AODPromotedNotification(aodPromotedNotificationViewModelFactory)
+ }
}
@Composable
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
index 6738b97..1423d4a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneTransitionLayoutDataSource.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.systemui.scene.ui.composable
import androidx.compose.runtime.snapshotFlow
@@ -26,7 +24,6 @@
import com.android.compose.animation.scene.observableTransitionState
import com.android.systemui.scene.shared.model.SceneDataSource
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
index 51483a8..358d018 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/nestedscroll/PriorityNestedScrollConnectionTest.kt
@@ -14,8 +14,6 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
package com.android.compose.nestedscroll
import androidx.compose.foundation.gestures.FlingBehavior
@@ -28,7 +26,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.test.runMonotonicClockTest
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
index 0245cf2..97fba3d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/test/RunMonotonicClockTest.kt
@@ -3,7 +3,6 @@
import androidx.compose.ui.test.ExperimentalTestApi
import androidx.compose.ui.test.TestMonotonicFrameClock
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runTest
@@ -18,7 +17,7 @@
* Note: Please refer to the documentation for [runTest], as this feature utilizes it. This will
* provide a comprehensive understanding of all its behaviors.
*/
-@OptIn(ExperimentalTestApi::class, ExperimentalCoroutinesApi::class)
+@OptIn(ExperimentalTestApi::class)
fun runMonotonicClockTest(block: suspend MonotonicClockTestScope.() -> Unit) = runTest {
val testScope: TestScope = this
diff --git a/packages/SystemUI/flag_check.py b/packages/SystemUI/flag_check.py
deleted file mode 100755
index d78ef5a..0000000
--- a/packages/SystemUI/flag_check.py
+++ /dev/null
@@ -1,138 +0,0 @@
-#! /usr/bin/env python3
-
-import sys
-import re
-import argparse
-
-# partially copied from tools/repohooks/rh/hooks.py
-
-TEST_MSG = """Commit message is missing a "Flag:" line. It must match one of the
-following case-sensitive regex:
-
- %s
-
-The Flag: stanza is regex matched and should describe whether your change is behind a flag or flags.
-As a CL author, you'll have a consistent place to describe the risk of the proposed change by explicitly calling out the name of the flag.
-For legacy flags use EXEMPT with your flag name.
-
-Some examples below:
-
-Flag: NONE Repohook Update
-Flag: TEST_ONLY
-Flag: EXEMPT resource only update
-Flag: EXEMPT bugfix
-Flag: EXEMPT refactor
-Flag: com.android.launcher3.enable_twoline_allapps
-Flag: com.google.android.apps.nexuslauncher.zero_state_web_data_loader
-
-Check the git history for more examples. It's a regex matched field. See go/android-flag-directive for more details on various formats.
-"""
-
-def main():
- """Check the commit message for a 'Flag:' line."""
- parser = argparse.ArgumentParser(
- description='Check the commit message for a Flag: line.')
- parser.add_argument('--msg',
- metavar='msg',
- type=str,
- nargs='?',
- default='HEAD',
- help='commit message to process.')
- parser.add_argument(
- '--files',
- metavar='files',
- nargs='?',
- default='',
- help=
- 'PREUPLOAD_FILES in repo upload to determine whether the check should run for the files.')
- parser.add_argument(
- '--project',
- metavar='project',
- type=str,
- nargs='?',
- default='',
- help=
- 'REPO_PROJECT in repo upload to determine whether the check should run for this project.')
-
- # Parse the arguments
- args = parser.parse_args()
- desc = args.msg
- files = args.files
- project = args.project
-
- if not should_run_path(project, files):
- return
-
- field = 'Flag'
- none = 'NONE'
- testOnly = 'TEST_ONLY'
- docsOnly = 'DOCS_ONLY'
- exempt = 'EXEMPT'
- justification = '<justification>'
-
- # Aconfig Flag name format = <packageName>.<flagName>
- # package name - Contains only lowercase alphabets + digits + '.' - Ex: com.android.launcher3
- # For now alphabets, digits, "_", "." characters are allowed in flag name.
- # Checks if there is "one dot" between packageName and flagName and not adding stricter format check
- #common_typos_disable
- flagName = '([a-zA-Z0-9.]+)([.]+)([a-zA-Z0-9_.]+)'
-
- # None and Exempt needs justification
- exemptRegex = fr'{exempt}\s*[a-zA-Z]+'
- noneRegex = fr'{none}\s*[a-zA-Z]+'
- #common_typos_enable
-
- readableRegexMsg = '\n\tFlag: '+none+' '+justification+'\n\tFlag: <packageName>.<flagName>\n\tFlag: ' +exempt+' '+justification+'\n\tFlag: '+testOnly+'\n\tFlag: '+docsOnly
-
- flagRegex = fr'^{field}: .*$'
- check_flag = re.compile(flagRegex) #Flag:
-
- # Ignore case for flag name format.
- flagNameRegex = fr'(?i)^{field}:\s*({noneRegex}|{flagName}|{testOnly}|{docsOnly}|{exemptRegex})\s*'
- check_flagName = re.compile(flagNameRegex) #Flag: <flag name format>
-
- flagError = False
- foundFlag = []
- # Check for multiple "Flag:" lines and all lines should match this format
- for line in desc.splitlines():
- if check_flag.match(line):
- if not check_flagName.match(line):
- flagError = True
- break
- foundFlag.append(line)
-
- # Throw error if
- # 1. No "Flag:" line is found
- # 2. "Flag:" doesn't follow right format.
- if (not foundFlag) or (flagError):
- error = TEST_MSG % (readableRegexMsg)
- print(error)
- sys.exit(1)
-
- sys.exit(0)
-
-
-def should_run_path(project, files):
- """Returns a boolean if this check should run with these paths.
- If you want to check for a particular subdirectory under the path,
- add a check here, call should_run_files and check for a specific sub dir path in should_run_files.
- """
- if not project:
- return False
- if project == 'platform/frameworks/base':
- return should_run_files(files)
- # Default case, run for all other projects which calls this script.
- return True
-
-
-def should_run_files(files):
- """Returns a boolean if this check should run with these files."""
- if not files:
- return False
- if 'packages/SystemUI' in files:
- return True
- return False
-
-
-if __name__ == '__main__':
- main()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
index 0d410cf..b6359c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalPrefsRepositoryImplTest.kt
@@ -116,6 +116,34 @@
}
@Test
+ fun isDreamButtonTooltipDismissedValue_byDefault_isFalse() =
+ testScope.runTest {
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
+ @Test
+ fun isDreamButtonTooltipDismissedValue_onSet_isTrue() =
+ testScope.runTest {
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+
+ underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+ assertThat(isDreamButtonTooltipDismissed).isTrue()
+ }
+
+ @Test
+ fun isDreamButtonTooltipDismissedValue_onSetForDifferentUser_isStillFalse() =
+ testScope.runTest {
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed(MAIN_USER))
+
+ underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
+ @Test
fun getSharedPreferences_whenFileRestored() =
testScope.runTest {
val isCtaDismissed by collectLastValue(underTest.isCtaDismissed(MAIN_USER))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 809099e..eb1f1d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -38,19 +38,19 @@
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled
import com.android.systemui.communal.shared.model.CommunalBackgroundType
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -62,9 +62,11 @@
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalSettingsRepositoryImplTest(flags: FlagsParameterization?) : SysuiTestCase() {
private val kosmos =
- testKosmos().apply { mainResources = mContext.orCreateTestableResources.resources }
- private val testScope = kosmos.testScope
- private lateinit var underTest: CommunalSettingsRepository
+ testKosmos()
+ .apply { mainResources = mContext.orCreateTestableResources.resources }
+ .useUnconfinedTestDispatcher()
+
+ private val Kosmos.underTest by Kosmos.Fixture { communalSettingsRepository }
init {
mSetFlagsRule.setFlagsParameterization(flags!!)
@@ -76,98 +78,105 @@
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(SECONDARY_USER, KEYGUARD_DISABLE_FEATURES_NONE)
setKeyguardFeaturesDisabled(WORK_PROFILE, KEYGUARD_DISABLE_FEATURES_NONE)
- underTest = kosmos.communalSettingsRepository
}
@EnableFlags(FLAG_COMMUNAL_HUB)
@DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_bothEnabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ fun getFlagEnabled_bothEnabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- assertThat(underTest.getFlagEnabled()).isTrue()
- }
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
@DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_bothDisabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ fun getFlagEnabled_bothDisabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_onlyClassicFlagEnabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
+ fun getFlagEnabled_onlyClassicFlagEnabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_COMMUNAL_HUB)
@DisableFlags(FLAG_GLANCEABLE_HUB_V2)
@Test
- fun getFlagEnabled_onlyTrunkFlagEnabled() {
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ fun getFlagEnabled_onlyTrunkFlagEnabled() =
+ kosmos.runTest {
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@DisableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_mobileConfigEnabled() {
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- true,
- )
+ fun getFlagEnabled_mobileConfigEnabled() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
- assertThat(underTest.getFlagEnabled()).isTrue()
- }
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
@DisableFlags(FLAG_GLANCEABLE_HUB_V2, FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_onlyMobileConfigEnabled() {
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- true,
- )
+ fun getFlagEnabled_onlyMobileConfigEnabled() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@DisableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_onlyMobileFlagEnabled() {
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- false,
- )
+ fun getFlagEnabled_onlyMobileFlagEnabled() =
+ kosmos.runTest {
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ false,
+ )
- assertThat(underTest.getFlagEnabled()).isFalse()
- }
+ assertThat(underTest.getFlagEnabled()).isFalse()
+ }
@EnableFlags(FLAG_GLANCEABLE_HUB_V2)
@DisableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun getFlagEnabled_oldFlagIgnored() {
- // New config flag enabled.
- mContext.orCreateTestableResources.addOverride(
- com.android.internal.R.bool.config_glanceableHubEnabled,
- true,
- )
+ fun getFlagEnabled_oldFlagIgnored() =
+ kosmos.runTest {
+ // New config flag enabled.
+ mContext.orCreateTestableResources.addOverride(
+ com.android.internal.R.bool.config_glanceableHubEnabled,
+ true,
+ )
- // Old config flag disabled.
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
+ // Old config flag disabled.
+ fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
- assertThat(underTest.getFlagEnabled()).isTrue()
- }
+ assertThat(underTest.getFlagEnabled()).isTrue()
+ }
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun secondaryUserIsInvalid() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(SECONDARY_USER))
assertThat(enabledState?.enabled).isFalse()
@@ -187,7 +196,7 @@
@DisableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
@Test
fun communalHubFlagIsDisabled() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_FLAG)
@@ -196,35 +205,23 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsDisabledByUser() =
- testScope.runTest {
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 0,
- PRIMARY_USER.id,
- )
+ kosmos.runTest {
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isFalse()
assertThat(enabledState).containsExactly(DisabledReason.DISABLED_REASON_USER_SETTING)
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 1,
- SECONDARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, SECONDARY_USER.id)
assertThat(enabledState?.enabled).isFalse()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 1,
- PRIMARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 1, PRIMARY_USER.id)
assertThat(enabledState?.enabled).isTrue()
}
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsDisabledByDevicePolicy() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isTrue()
@@ -236,7 +233,7 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun widgetsAllowedForWorkProfile_isFalse_whenDisallowedByDevicePolicy() =
- testScope.runTest {
+ kosmos.runTest {
val widgetsAllowedForWorkProfile by
collectLastValue(underTest.getAllowedByDevicePolicy(WORK_PROFILE))
assertThat(widgetsAllowedForWorkProfile).isTrue()
@@ -248,7 +245,7 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsEnabled_whenDisallowedByDevicePolicyForWorkProfile() =
- testScope.runTest {
+ kosmos.runTest {
val enabledStateForPrimaryUser by
collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledStateForPrimaryUser?.enabled).isTrue()
@@ -260,15 +257,11 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
fun hubIsDisabledByUserAndDevicePolicy() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getEnabledState(PRIMARY_USER))
assertThat(enabledState?.enabled).isTrue()
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.GLANCEABLE_HUB_ENABLED,
- 0,
- PRIMARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.GLANCEABLE_HUB_ENABLED, 0, PRIMARY_USER.id)
setKeyguardFeaturesDisabled(PRIMARY_USER, KEYGUARD_DISABLE_WIDGETS_ALL)
assertThat(enabledState?.enabled).isFalse()
@@ -282,17 +275,17 @@
@Test
@DisableFlags(FLAG_GLANCEABLE_HUB_BLURRED_BACKGROUND)
fun backgroundType_defaultValue() =
- testScope.runTest {
+ kosmos.runTest {
val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
assertThat(backgroundType).isEqualTo(CommunalBackgroundType.ANIMATED)
}
@Test
fun backgroundType_verifyAllValues() =
- testScope.runTest {
+ kosmos.runTest {
val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
for (type in CommunalBackgroundType.entries) {
- kosmos.fakeSettings.putIntForUser(
+ fakeSettings.putIntForUser(
GLANCEABLE_HUB_BACKGROUND_SETTING,
type.value,
PRIMARY_USER.id,
@@ -308,30 +301,71 @@
@Test
fun screensaverDisabledByUser() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
- 0,
- PRIMARY_USER.id,
- )
+ fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 0, PRIMARY_USER.id)
assertThat(enabledState).isFalse()
}
@Test
fun screensaverEnabledByUser() =
- testScope.runTest {
+ kosmos.runTest {
val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
- kosmos.fakeSettings.putIntForUser(
- Settings.Secure.SCREENSAVER_ENABLED,
+ fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ENABLED, 1, PRIMARY_USER.id)
+
+ assertThat(enabledState).isTrue()
+ }
+
+ @Test
+ fun whenToDream_charging() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
1,
PRIMARY_USER.id,
)
- assertThat(enabledState).isTrue()
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_CHARGING)
+ }
+
+ @Test
+ fun whenToDream_docked() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_DOCKED)
+ }
+
+ @Test
+ fun whenToDream_postured() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.WHILE_POSTURED)
+ }
+
+ @Test
+ fun whenToDream_default() =
+ kosmos.runTest {
+ val whenToDreamState by collectLastValue(underTest.getWhenToDreamState(PRIMARY_USER))
+ assertThat(whenToDreamState).isEqualTo(WhenToDream.NEVER)
}
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
index 7ae0577..c9e7a5d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt
@@ -38,13 +38,9 @@
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.common.data.repository.fake
import com.android.systemui.communal.data.model.CommunalSmartspaceTimer
-import com.android.systemui.communal.data.repository.FakeCommunalMediaRepository
-import com.android.systemui.communal.data.repository.FakeCommunalPrefsRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSceneRepository
-import com.android.systemui.communal.data.repository.FakeCommunalSmartspaceRepository
-import com.android.systemui.communal.data.repository.FakeCommunalTutorialRepository
-import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepository
import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository
import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository
@@ -53,52 +49,49 @@
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel
+import com.android.systemui.communal.posturing.data.repository.fake
+import com.android.systemui.communal.posturing.data.repository.posturingRepository
+import com.android.systemui.communal.posturing.shared.model.PosturedState
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.EditModeState
-import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
-import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.fakeDockManager
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
-import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.plugins.activityStarter
-import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.Scenes
-import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.statusbar.phone.fakeManagedProfileController
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
-import com.android.systemui.utils.leaks.FakeManagedProfileController
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.advanceTimeBy
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
@@ -107,32 +100,15 @@
* [CommunalInteractorCommunalDisabledTest].
*/
@SmallTest
-@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(ParameterizedAndroidJunit4::class)
class CommunalInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
- @Mock private lateinit var mainUser: UserInfo
- @Mock private lateinit var secondaryUser: UserInfo
+ private val mainUser =
+ UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
+ private val secondaryUser = UserInfo(/* id= */ 1, /* name= */ "secondary user", /* flags= */ 0)
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private lateinit var tutorialRepository: FakeCommunalTutorialRepository
- private lateinit var communalRepository: FakeCommunalSceneRepository
- private lateinit var mediaRepository: FakeCommunalMediaRepository
- private lateinit var widgetRepository: FakeCommunalWidgetRepository
- private lateinit var smartspaceRepository: FakeCommunalSmartspaceRepository
- private lateinit var userRepository: FakeUserRepository
- private lateinit var keyguardRepository: FakeKeyguardRepository
- private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository
- private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter
- private lateinit var sceneInteractor: SceneInteractor
- private lateinit var communalSceneInteractor: CommunalSceneInteractor
- private lateinit var userTracker: FakeUserTracker
- private lateinit var activityStarter: ActivityStarter
- private lateinit var userManager: UserManager
- private lateinit var managedProfileController: FakeManagedProfileController
-
- private lateinit var underTest: CommunalInteractor
+ private val Kosmos.underTest by Kosmos.Fixture { communalInteractor }
init {
mSetFlagsRule.setFlagsParameterization(flags)
@@ -140,128 +116,104 @@
@Before
fun setUp() {
- MockitoAnnotations.initMocks(this)
-
- tutorialRepository = kosmos.fakeCommunalTutorialRepository
- communalRepository = kosmos.fakeCommunalSceneRepository
- mediaRepository = kosmos.fakeCommunalMediaRepository
- widgetRepository = kosmos.fakeCommunalWidgetRepository
- smartspaceRepository = kosmos.fakeCommunalSmartspaceRepository
- userRepository = kosmos.fakeUserRepository
- keyguardRepository = kosmos.fakeKeyguardRepository
- editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter
- communalPrefsRepository = kosmos.fakeCommunalPrefsRepository
- sceneInteractor = kosmos.sceneInteractor
- communalSceneInteractor = kosmos.communalSceneInteractor
- userTracker = kosmos.fakeUserTracker
- activityStarter = kosmos.activityStarter
- userManager = kosmos.userManager
- managedProfileController = kosmos.fakeManagedProfileController
-
- whenever(mainUser.isMain).thenReturn(true)
- whenever(secondaryUser.isMain).thenReturn(false)
- whenever(userManager.isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
- whenever(userManager.isManagedProfile(anyInt())).thenReturn(false)
- userRepository.setUserInfos(listOf(mainUser, secondaryUser))
+ whenever(kosmos.userManager.isQuietModeEnabled(any<UserHandle>())).thenReturn(false)
+ whenever(kosmos.userManager.isManagedProfile(anyInt())).thenReturn(false)
+ kosmos.fakeUserRepository.setUserInfos(listOf(mainUser, secondaryUser))
kosmos.fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, true)
mSetFlagsRule.enableFlags(FLAG_COMMUNAL_HUB)
-
- underTest = kosmos.communalInteractor
}
@Test
fun communalEnabled_true() =
- testScope.runTest {
- userRepository.setSelectedUserInfo(mainUser)
- runCurrent()
+ kosmos.runTest {
+ fakeUserRepository.setSelectedUserInfo(mainUser)
assertThat(underTest.isCommunalEnabled.value).isTrue()
}
@Test
fun isCommunalAvailable_storageUnlockedAndMainUser_true() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isTrue()
}
@Test
fun isCommunalAvailable_storageLockedAndMainUser_false() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(true)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(true)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isFalse()
}
@Test
fun isCommunalAvailable_storageUnlockedAndSecondaryUser_false() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(secondaryUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(secondaryUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isFalse()
}
@Test
fun isCommunalAvailable_whenKeyguardShowing_true() =
- testScope.runTest {
+ kosmos.runTest {
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isTrue()
}
@Test
fun isCommunalAvailable_communalDisabled_false() =
- testScope.runTest {
+ kosmos.runTest {
mSetFlagsRule.disableFlags(FLAG_COMMUNAL_HUB, FLAG_GLANCEABLE_HUB_V2)
val isAvailable by collectLastValue(underTest.isCommunalAvailable)
assertThat(isAvailable).isFalse()
- keyguardRepository.setIsEncryptedOrLockdown(false)
- userRepository.setSelectedUserInfo(mainUser)
- keyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
assertThat(isAvailable).isFalse()
}
@Test
fun widget_tutorialCompletedAndWidgetsAvailable_showWidgetContent() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
val widgetContent by collectLastValue(underTest.widgetContent)
@@ -356,18 +308,18 @@
totalTargets: Int,
expectedSizes: List<CommunalContentSize>,
) =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val targets = mutableListOf<CommunalSmartspaceTimer>()
for (index in 0 until totalTargets) {
targets.add(smartspaceTimer(index.toString()))
}
- smartspaceRepository.setTimers(targets)
+ fakeCommunalSmartspaceRepository.setTimers(targets)
val smartspaceContent by collectLastValue(underTest.ongoingContent(false))
assertThat(smartspaceContent?.size).isEqualTo(totalTargets)
@@ -378,12 +330,12 @@
@Test
fun umo_mediaPlaying_showsUmo() =
- testScope.runTest {
+ kosmos.runTest {
// Tutorial completed.
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Media is playing.
- mediaRepository.mediaActive()
+ fakeCommunalMediaRepository.mediaActive()
val umoContent by collectLastValue(underTest.ongoingContent(true))
@@ -394,12 +346,12 @@
@Test
fun umo_mediaPlaying_mediaHostNotVisible_hidesUmo() =
- testScope.runTest {
+ kosmos.runTest {
// Tutorial completed.
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Media is playing.
- mediaRepository.mediaActive()
+ fakeCommunalMediaRepository.mediaActive()
val umoContent by collectLastValue(underTest.ongoingContent(false))
assertThat(umoContent?.size).isEqualTo(0)
@@ -409,26 +361,26 @@
@Test
@DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun ongoing_shouldOrderAndSizeByTimestamp() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Timer1 started
val timer1 = smartspaceTimer("timer1", timestamp = 1L)
- smartspaceRepository.setTimers(listOf(timer1))
+ fakeCommunalSmartspaceRepository.setTimers(listOf(timer1))
// Umo started
- mediaRepository.mediaActive(timestamp = 2L)
+ fakeCommunalMediaRepository.mediaActive(timestamp = 2L)
// Timer2 started
val timer2 = smartspaceTimer("timer2", timestamp = 3L)
- smartspaceRepository.setTimers(listOf(timer1, timer2))
+ fakeCommunalSmartspaceRepository.setTimers(listOf(timer1, timer2))
// Timer3 started
val timer3 = smartspaceTimer("timer3", timestamp = 4L)
- smartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
+ fakeCommunalSmartspaceRepository.setTimers(listOf(timer1, timer2, timer3))
val ongoingContent by collectLastValue(underTest.ongoingContent(true))
assertThat(ongoingContent?.size).isEqualTo(4)
@@ -447,8 +399,8 @@
@Test
fun ctaTile_showsByDefault() =
- testScope.runTest {
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ kosmos.runTest {
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val ctaTileContent by collectLastValue(underTest.ctaTileContent)
@@ -461,14 +413,13 @@
@Test
fun ctaTile_afterDismiss_doesNotShow() =
- testScope.runTest {
+ kosmos.runTest {
// Set to main user, so we can dismiss the tile for the main user.
- val user = userRepository.asMainUser()
- userTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
- runCurrent()
+ val user = fakeUserRepository.asMainUser()
+ fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
- communalPrefsRepository.setCtaDismissed(user)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeCommunalPrefsRepository.setCtaDismissed(user)
val ctaTileContent by collectLastValue(underTest.ctaTileContent)
@@ -477,36 +428,30 @@
@Test
fun listensToSceneChange() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
- runCurrent()
- var desiredScene = collectLastValue(underTest.desiredScene)
- runCurrent()
- assertThat(desiredScene()).isEqualTo(CommunalScenes.Blank)
+ val desiredScene by collectLastValue(underTest.desiredScene)
+ assertThat(desiredScene).isEqualTo(CommunalScenes.Blank)
val targetScene = CommunalScenes.Communal
- communalRepository.changeScene(targetScene)
- desiredScene = collectLastValue(underTest.desiredScene)
- runCurrent()
- assertThat(desiredScene()).isEqualTo(targetScene)
+ fakeCommunalSceneRepository.changeScene(targetScene)
+ assertThat(desiredScene).isEqualTo(targetScene)
}
@Test
fun updatesScene() =
- testScope.runTest {
+ kosmos.runTest {
val targetScene = CommunalScenes.Communal
-
underTest.changeScene(targetScene, "test")
- val desiredScene = collectLastValue(communalRepository.currentScene)
- runCurrent()
- assertThat(desiredScene()).isEqualTo(targetScene)
+ val desiredScene by collectLastValue(fakeCommunalSceneRepository.currentScene)
+ assertThat(desiredScene).isEqualTo(targetScene)
}
@Test
fun transitionProgress_onTargetScene_fullProgress() =
- testScope.runTest {
+ kosmos.runTest {
val targetScene = CommunalScenes.Blank
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
val transitionProgress by collectLastValue(transitionProgressFlow)
@@ -524,7 +469,7 @@
@Test
fun transitionProgress_notOnTargetScene_noProgress() =
- testScope.runTest {
+ kosmos.runTest {
val targetScene = CommunalScenes.Blank
val currentScene = CommunalScenes.Communal
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
@@ -543,7 +488,7 @@
@Test
fun transitionProgress_transitioningToTrackedScene() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene = CommunalScenes.Communal
val targetScene = CommunalScenes.Blank
val transitionProgressFlow = underTest.transitionProgressToScene(targetScene)
@@ -591,7 +536,7 @@
@Test
fun transitionProgress_transitioningAwayFromTrackedScene() =
- testScope.runTest {
+ kosmos.runTest {
val currentScene = CommunalScenes.Blank
val targetScene = CommunalScenes.Communal
val transitionProgressFlow = underTest.transitionProgressToScene(currentScene)
@@ -642,52 +587,42 @@
@Test
fun isCommunalShowing() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
- runCurrent()
- var isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
- runCurrent()
- assertThat(isCommunalShowing()).isEqualTo(false)
+ val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
+ assertThat(isCommunalShowing).isEqualTo(false)
underTest.changeScene(CommunalScenes.Communal, "test")
-
- isCommunalShowing = collectLastValue(underTest.isCommunalShowing)
- runCurrent()
- assertThat(isCommunalShowing()).isEqualTo(true)
+ assertThat(isCommunalShowing).isEqualTo(true)
}
@Test
fun isCommunalShowing_whenSceneContainerDisabled() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
- runCurrent()
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
- runCurrent()
assertThat(isCommunalShowing).isFalse()
// Verify scene changes with the flag doesn't have any impact
sceneInteractor.changeScene(Scenes.Communal, loggingReason = "")
- runCurrent()
assertThat(isCommunalShowing).isFalse()
// Verify scene changes (without the flag) to communal sets the value to true
underTest.changeScene(CommunalScenes.Communal, "test")
- runCurrent()
assertThat(isCommunalShowing).isTrue()
// Verify scene changes (without the flag) to blank sets the value back to false
underTest.changeScene(CommunalScenes.Blank, "test")
- runCurrent()
assertThat(isCommunalShowing).isFalse()
}
@Test
@EnableSceneContainer
fun isCommunalShowing_whenSceneContainerEnabled() =
- testScope.runTest {
+ kosmos.runTest {
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
assertThat(isCommunalShowing).isFalse()
@@ -704,7 +639,7 @@
@Test
@EnableSceneContainer
fun isCommunalShowing_whenSceneContainerEnabledAndChangeToLegacyScene() =
- testScope.runTest {
+ kosmos.runTest {
// Verify default is false
val isCommunalShowing by collectLastValue(underTest.isCommunalShowing)
assertThat(isCommunalShowing).isFalse()
@@ -720,21 +655,19 @@
@Test
fun isIdleOnCommunal() =
- testScope.runTest {
+ kosmos.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(CommunalScenes.Blank)
)
- communalRepository.setTransitionState(transitionState)
+ fakeCommunalSceneRepository.setTransitionState(transitionState)
// isIdleOnCommunal is false when not on communal.
val isIdleOnCommunal by collectLastValue(underTest.isIdleOnCommunal)
- runCurrent()
assertThat(isIdleOnCommunal).isEqualTo(false)
// Transition to communal.
transitionState.value = ObservableTransitionState.Idle(CommunalScenes.Communal)
- runCurrent()
// isIdleOnCommunal is now true since we're on communal.
assertThat(isIdleOnCommunal).isEqualTo(true)
@@ -749,7 +682,6 @@
isInitiatedByUserInput = false,
isUserInputOngoing = flowOf(false),
)
- runCurrent()
// isIdleOnCommunal turns false as soon as transition away starts.
assertThat(isIdleOnCommunal).isEqualTo(false)
@@ -757,12 +689,12 @@
@Test
fun isCommunalVisible() =
- testScope.runTest {
+ kosmos.runTest {
val transitionState =
MutableStateFlow<ObservableTransitionState>(
ObservableTransitionState.Idle(CommunalScenes.Blank)
)
- communalRepository.setTransitionState(transitionState)
+ fakeCommunalSceneRepository.setTransitionState(transitionState)
// isCommunalVisible is false when not on communal.
val isCommunalVisible by collectLastValue(underTest.isCommunalVisible)
@@ -805,7 +737,7 @@
@Test
fun testShowWidgetEditorStartsActivity() =
- testScope.runTest {
+ kosmos.runTest {
val editModeState by collectLastValue(communalSceneInteractor.editModeState)
underTest.showWidgetEditor()
@@ -816,14 +748,14 @@
@Test
fun showWidgetEditor_openWidgetPickerOnStart_startsActivity() =
- testScope.runTest {
+ kosmos.runTest {
underTest.showWidgetEditor(shouldOpenWidgetPickerOnStart = true)
verify(editWidgetsActivityStarter).startActivity(shouldOpenWidgetPickerOnStart = true)
}
@Test
fun navigateToCommunalWidgetSettings_startsActivity() =
- testScope.runTest {
+ kosmos.runTest {
underTest.navigateToCommunalWidgetSettings()
val intentCaptor = argumentCaptor<Intent>()
verify(activityStarter)
@@ -833,23 +765,22 @@
@Test
fun filterWidgets_whenUserProfileRemoved() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Only main user exists.
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
val widgetContent by collectLastValue(underTest.widgetContent)
// Given three widgets, and one of them is associated with pre-existing work profile.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
// One widget is filtered out and the remaining two link to main user id.
assertThat(checkNotNull(widgetContent).size).isEqualTo(2)
@@ -867,17 +798,16 @@
@Test
fun widgetContent_inQuietMode() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
// Work profile is set up.
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// When work profile is paused.
whenever(userManager.isQuietModeEnabled(eq(UserHandle.of(USER_INFO_WORK.id))))
@@ -885,9 +815,9 @@
whenever(userManager.isManagedProfile(eq(USER_INFO_WORK.id))).thenReturn(true)
val widgetContent by collectLastValue(underTest.widgetContent)
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 2, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
// The work profile widget is in quiet mode, while other widgets are not.
assertThat(widgetContent).hasSize(3)
@@ -911,23 +841,25 @@
@Test
fun filterWidgets_whenDisallowedByDevicePolicyForWorkProfile() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
val widgetContent by collectLastValue(underTest.widgetContent)
// One available work widget, one pending work widget, and one regular available widget.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addPendingWidget(
+ appWidgetId = 2,
+ userId = USER_INFO_WORK.id,
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
setKeyguardFeaturesDisabled(
USER_INFO_WORK,
@@ -941,23 +873,25 @@
@Test
fun filterWidgets_whenAllowedByDevicePolicyForWorkProfile() =
- testScope.runTest {
+ kosmos.runTest {
// Keyguard showing, and tutorial completed.
- keyguardRepository.setKeyguardShowing(true)
- keyguardRepository.setKeyguardOccluded(false)
- tutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeKeyguardRepository.setKeyguardOccluded(false)
+ fakeCommunalTutorialRepository.setTutorialSettingState(HUB_MODE_TUTORIAL_COMPLETED)
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- userRepository.setSelectedUserInfo(MAIN_USER_INFO)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
+ fakeUserRepository.setSelectedUserInfo(MAIN_USER_INFO)
val widgetContent by collectLastValue(underTest.widgetContent)
// Given three widgets, and one of them is associated with work profile.
- widgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
- widgetRepository.addPendingWidget(appWidgetId = 2, userId = USER_INFO_WORK.id)
- widgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 1, userId = USER_INFO_WORK.id)
+ fakeCommunalWidgetRepository.addPendingWidget(
+ appWidgetId = 2,
+ userId = USER_INFO_WORK.id,
+ )
+ fakeCommunalWidgetRepository.addWidget(appWidgetId = 3, userId = MAIN_USER_INFO.id)
setKeyguardFeaturesDisabled(
USER_INFO_WORK,
@@ -973,7 +907,7 @@
@Test
fun showCommunalFromOccluded_enteredOccludedFromHub() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -989,7 +923,7 @@
@Test
fun showCommunalFromOccluded_enteredOccludedFromLockscreen() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1005,7 +939,7 @@
@Test
fun showCommunalFromOccluded_communalBecomesUnavailableWhileOccluded() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1015,7 +949,6 @@
to = KeyguardState.OCCLUDED,
testScope,
)
- runCurrent()
kosmos.setCommunalAvailable(false)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1023,7 +956,7 @@
@Test
fun showCommunalFromOccluded_showBouncerWhileOccluded() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1033,7 +966,6 @@
to = KeyguardState.OCCLUDED,
testScope,
)
- runCurrent()
kosmos.fakeKeyguardTransitionRepository.sendTransitionSteps(
from = KeyguardState.OCCLUDED,
to = KeyguardState.PRIMARY_BOUNCER,
@@ -1045,7 +977,7 @@
@Test
fun showCommunalFromOccluded_enteredOccludedFromDreaming() =
- testScope.runTest {
+ kosmos.runTest {
kosmos.setCommunalAvailable(true)
val showCommunalFromOccluded by collectLastValue(underTest.showCommunalFromOccluded)
assertThat(showCommunalFromOccluded).isFalse()
@@ -1069,7 +1001,7 @@
@Test
fun dismissDisclaimerSetsDismissedFlag() =
- testScope.runTest {
+ kosmos.runTest {
val disclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
assertThat(disclaimerDismissed).isFalse()
underTest.setDisclaimerDismissed()
@@ -1078,17 +1010,17 @@
@Test
fun dismissDisclaimerTimeoutResetsDismissedFlag() =
- testScope.runTest {
+ kosmos.runTest {
val disclaimerDismissed by collectLastValue(underTest.isDisclaimerDismissed)
underTest.setDisclaimerDismissed()
assertThat(disclaimerDismissed).isTrue()
- advanceTimeBy(CommunalInteractor.DISCLAIMER_RESET_MILLIS)
+ testScope.advanceTimeBy(CommunalInteractor.DISCLAIMER_RESET_MILLIS)
assertThat(disclaimerDismissed).isFalse()
}
@Test
fun settingSelectedKey_flowUpdated() {
- testScope.runTest {
+ kosmos.runTest {
val key = "test"
val selectedKey by collectLastValue(underTest.selectedKey)
underTest.setSelectedKey(key)
@@ -1098,36 +1030,35 @@
@Test
fun unpauseWorkProfileEnablesWorkMode() =
- testScope.runTest {
+ kosmos.runTest {
underTest.unpauseWorkProfile()
- assertThat(managedProfileController.isWorkModeEnabled()).isTrue()
+ assertThat(fakeManagedProfileController.isWorkModeEnabled()).isTrue()
}
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
@DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_withoutUpdatingOrder() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1159,26 +1090,25 @@
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_withoutUpdatingOrder_responsive() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1211,26 +1141,25 @@
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING)
@DisableFlags(FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_andUpdateOrder() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = CommunalContentSize.FixedSize.HALF.span,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1266,26 +1195,25 @@
@Test
@EnableFlags(FLAG_COMMUNAL_WIDGET_RESIZING, FLAG_COMMUNAL_RESPONSIVE_GRID)
fun resizeWidget_andUpdateOrder_responsive() =
- testScope.runTest {
+ kosmos.runTest {
val userInfos = listOf(MAIN_USER_INFO)
- userRepository.setUserInfos(userInfos)
- userTracker.set(userInfos = userInfos, selectedUserIndex = 0)
- runCurrent()
+ fakeUserRepository.setUserInfos(userInfos)
+ fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
// Widgets available.
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 1,
userId = MAIN_USER_INFO.id,
rank = 0,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 2,
userId = MAIN_USER_INFO.id,
rank = 1,
spanY = 1,
)
- widgetRepository.addWidget(
+ fakeCommunalWidgetRepository.addWidget(
appWidgetId = 3,
userId = MAIN_USER_INFO.id,
rank = 2,
@@ -1318,6 +1246,66 @@
.inOrder()
}
+ @Test
+ fun showCommunalWhileCharging() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ 1,
+ mainUser.id,
+ )
+
+ val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+ batteryRepository.fake.setDevicePluggedIn(false)
+ assertThat(shouldShowCommunal).isFalse()
+
+ batteryRepository.fake.setDevicePluggedIn(true)
+ assertThat(shouldShowCommunal).isTrue()
+ }
+
+ @Test
+ fun showCommunalWhilePosturedAndCharging() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ 1,
+ mainUser.id,
+ )
+
+ val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+ batteryRepository.fake.setDevicePluggedIn(true)
+ posturingRepository.fake.setPosturedState(PosturedState.NotPostured)
+ assertThat(shouldShowCommunal).isFalse()
+
+ posturingRepository.fake.setPosturedState(PosturedState.Postured(1f))
+ assertThat(shouldShowCommunal).isTrue()
+ }
+
+ @Test
+ fun showCommunalWhileDocked() =
+ kosmos.runTest {
+ fakeKeyguardRepository.setIsEncryptedOrLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(mainUser)
+ fakeKeyguardRepository.setKeyguardShowing(true)
+ fakeSettings.putIntForUser(Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 1, mainUser.id)
+
+ batteryRepository.fake.setDevicePluggedIn(true)
+ fakeDockManager.setIsDocked(false)
+
+ val shouldShowCommunal by collectLastValue(underTest.shouldShowCommunal)
+ assertThat(shouldShowCommunal).isFalse()
+
+ fakeDockManager.setIsDocked(true)
+ fakeDockManager.setDockEvent(DockManager.STATE_DOCKED)
+ assertThat(shouldShowCommunal).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
index 1fef693..1f5f8ce 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractorTest.kt
@@ -108,6 +108,43 @@
assertThat(isHubOnboardingDismissed).isFalse()
}
+ @Test
+ fun setDreamButtonTooltipDismissed_currentUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+ assertThat(isDreamButtonTooltipDismissed).isTrue()
+ }
+
+ @Test
+ fun setDreamButtonTooltipDismissed_anotherUser() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ underTest.setDreamButtonTooltipDismissed(SECONDARY_USER)
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
+ @Test
+ fun isDreamButtonTooltipDismissed_userSwitch() =
+ testScope.runTest {
+ setSelectedUser(MAIN_USER)
+ underTest.setDreamButtonTooltipDismissed(MAIN_USER)
+ val isDreamButtonTooltipDismissed by
+ collectLastValue(underTest.isDreamButtonTooltipDismissed)
+
+ assertThat(isDreamButtonTooltipDismissed).isTrue()
+ setSelectedUser(SECONDARY_USER)
+ assertThat(isDreamButtonTooltipDismissed).isFalse()
+ }
+
private suspend fun setSelectedUser(user: UserInfo) {
with(kosmos.fakeUserRepository) {
setUserInfos(listOf(user))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
index e4916b1..310bf64 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorTest.kt
@@ -21,19 +21,17 @@
import android.content.Intent
import android.content.pm.UserInfo
import android.os.UserManager
-import android.os.userManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.FakeUserRepository
import com.android.systemui.user.data.repository.fakeUserRepository
-import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotNull
import org.junit.Assert.assertNull
@@ -48,34 +46,20 @@
@RunWith(AndroidJUnit4::class)
class CommunalSettingsInteractorTest : SysuiTestCase() {
- private lateinit var userManager: UserManager
- private lateinit var userRepository: FakeUserRepository
- private lateinit var userTracker: FakeUserTracker
+ private val kosmos = testKosmos().useUnconfinedTestDispatcher()
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
-
- private lateinit var underTest: CommunalSettingsInteractor
+ private val Kosmos.underTest by Kosmos.Fixture { communalSettingsInteractor }
@Before
fun setUp() {
- userManager = kosmos.userManager
- userRepository = kosmos.fakeUserRepository
- userTracker = kosmos.fakeUserTracker
-
val userInfos = listOf(MAIN_USER_INFO, USER_INFO_WORK)
- userRepository.setUserInfos(userInfos)
- userTracker.set(
- userInfos = userInfos,
- selectedUserIndex = 0,
- )
-
- underTest = kosmos.communalSettingsInteractor
+ kosmos.fakeUserRepository.setUserInfos(userInfos)
+ kosmos.fakeUserTracker.set(userInfos = userInfos, selectedUserIndex = 0)
}
@Test
fun filterUsers_dontFilteredUsersWhenAllAreAllowed() =
- testScope.runTest {
+ kosmos.runTest {
// If no users have any keyguard features disabled...
val disallowedUser by
collectLastValue(underTest.workProfileUserDisallowedByDevicePolicy)
@@ -85,11 +69,11 @@
@Test
fun filterUsers_filterWorkProfileUserWhenDisallowed() =
- testScope.runTest {
+ kosmos.runTest {
// If the work profile user has keyguard widgets disabled...
setKeyguardFeaturesDisabled(
USER_INFO_WORK,
- DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+ DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL,
)
// ...then the disallowed user match the work profile
val disallowedUser by
@@ -102,7 +86,7 @@
whenever(
kosmos.devicePolicyManager.getKeyguardDisabledFeatures(
anyOrNull(),
- ArgumentMatchers.eq(user.id)
+ ArgumentMatchers.eq(user.id),
)
)
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
index 012ae8f..b747705 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.ui.viewmodel
+import android.content.pm.UserInfo
import android.platform.test.annotations.EnableFlags
import android.provider.Settings
import android.service.dream.dreamManager
@@ -24,15 +25,17 @@
import com.android.internal.logging.uiEventLoggerFake
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.SysuiTestCase
+import com.android.systemui.communal.data.repository.fakeCommunalPrefsRepository
+import com.android.systemui.communal.domain.interactor.HubOnboardingInteractorTest.Companion.MAIN_USER
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
-import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.plugins.activityStarter
+import com.android.systemui.settings.fakeUserTracker
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.testKosmos
import com.android.systemui.user.data.repository.fakeUserRepository
@@ -52,7 +55,6 @@
class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
private val kosmos = testKosmos()
private val testScope = kosmos.testScope
- private val uiEventLoggerFake = kosmos.uiEventLoggerFake
private val underTest: CommunalToDreamButtonViewModel by lazy {
kosmos.communalToDreamButtonViewModel
}
@@ -68,9 +70,9 @@
with(kosmos) {
runTest {
whenever(batteryController.isPluggedIn()).thenReturn(true)
+ runCurrent()
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isTrue()
+ assertThat(underTest.shouldShowDreamButtonOnHub).isTrue()
}
}
@@ -79,9 +81,9 @@
with(kosmos) {
runTest {
whenever(batteryController.isPluggedIn()).thenReturn(false)
+ runCurrent()
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isFalse()
+ assertThat(underTest.shouldShowDreamButtonOnHub).isFalse()
}
}
@@ -124,6 +126,23 @@
}
@Test
+ fun shouldShowDreamButtonTooltip_trueWhenNotDismissed() =
+ kosmos.runTest {
+ runCurrent()
+ assertThat(underTest.shouldShowTooltip).isTrue()
+ }
+
+ @Test
+ fun shouldShowDreamButtonTooltip_falseWhenDismissed() =
+ kosmos.runTest {
+ setSelectedUser(MAIN_USER)
+ fakeCommunalPrefsRepository.setDreamButtonTooltipDismissed(MAIN_USER)
+ runCurrent()
+
+ assertThat(underTest.shouldShowTooltip).isFalse()
+ }
+
+ @Test
fun onShowDreamButtonTap_eventLogged() =
with(kosmos) {
runTest {
@@ -134,4 +153,12 @@
.isEqualTo(CommunalUiEvent.COMMUNAL_HUB_SHOW_DREAM_BUTTON_TAP.id)
}
}
+
+ private suspend fun setSelectedUser(user: UserInfo) {
+ with(kosmos.fakeUserRepository) {
+ setUserInfos(listOf(user))
+ setSelectedUserInfo(user)
+ }
+ kosmos.fakeUserTracker.set(userInfos = listOf(user), selectedUserIndex = 0)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
index 6c955bf..5fd480f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractorTest.kt
@@ -176,14 +176,14 @@
}
@Test
- fun nonPowerButtonFPS_coExFaceFailure_doNotVibrateError() =
+ fun nonPowerButtonFPS_coExFaceFailure_vibrateError() =
testScope.runTest {
val playErrorHaptic by collectLastValue(underTest.playErrorHaptic)
enrollFingerprint(FingerprintSensorType.UDFPS_ULTRASONIC)
enrollFace()
runCurrent()
faceFailure()
- assertThat(playErrorHaptic).isNull()
+ assertThat(playErrorHaptic).isNotNull()
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index ef70305..af30e43 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -1149,7 +1149,7 @@
@Test
@DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun skipsFaceErrorHaptics_nonSfps_coEx() =
+ fun playsFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1161,14 +1161,15 @@
underTest.start()
updateFaceAuthStatus(isSuccess = false)
- assertThat(playErrorHaptic).isNull()
- verify(vibratorHelper, never()).vibrateAuthError(anyString())
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ verify(vibratorHelper).vibrateAuthError(anyString())
verify(vibratorHelper, never()).vibrateAuthSuccess(anyString())
}
@Test
@EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
- fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
+ fun playsMSDLFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
@@ -1180,9 +1181,10 @@
underTest.start()
updateFaceAuthStatus(isSuccess = false)
- assertThat(playErrorHaptic).isNull()
- assertThat(msdlPlayer.latestTokenPlayed).isNull()
- assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
index 2020d0d..3d31787 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -460,17 +460,17 @@
}
@Test
- public void testOnDisplayReady() {
- mCommandQueue.onDisplayReady(DEFAULT_DISPLAY);
+ public void testonDisplayAddSystemDecorations() {
+ mCommandQueue.onDisplayAddSystemDecorations(DEFAULT_DISPLAY);
waitForIdleSync();
- verify(mCallbacks).onDisplayReady(eq(DEFAULT_DISPLAY));
+ verify(mCallbacks).onDisplayAddSystemDecorations(eq(DEFAULT_DISPLAY));
}
@Test
- public void testOnDisplayReadyForSecondaryDisplay() {
- mCommandQueue.onDisplayReady(SECONDARY_DISPLAY);
+ public void testonDisplayAddSystemDecorationsForSecondaryDisplay() {
+ mCommandQueue.onDisplayAddSystemDecorations(SECONDARY_DISPLAY);
waitForIdleSync();
- verify(mCallbacks).onDisplayReady(eq(SECONDARY_DISPLAY));
+ verify(mCallbacks).onDisplayAddSystemDecorations(eq(SECONDARY_DISPLAY));
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
index 39c42f1..28b2ee8d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.kt
@@ -269,6 +269,36 @@
}
@Test
+ fun testOpenAndCloseGutsWithoutSave() {
+ val guts = spy(NotificationGuts(mContext))
+ whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
+ handler.post(((invocation.arguments[0] as Runnable)))
+ null
+ }
+
+ // Test doesn't support animation since the guts view is not attached.
+ doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any())
+
+ val realRow = createTestNotificationRow()
+ val menuItem = createTestMenuItem(realRow)
+
+ val row = spy(realRow)
+ whenever(row.windowToken).thenReturn(Binder())
+ whenever(row.guts).thenReturn(guts)
+
+ assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
+ executor.runAllReady()
+ verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
+
+ gutsManager.closeAndUndoGuts()
+
+ verify(guts).closeControls(anyInt(), anyInt(), eq(false), eq(false))
+ verify(row, times(1)).setGutsView(any<MenuItem>())
+ executor.runAllReady()
+ verify(headsUpManager).setGutsShown(realRow.entry, false)
+ }
+
+ @Test
fun testLockscreenShadeVisible_visible_gutsNotClosed() =
testScope.runTest {
// First, start out lockscreen or shade as not visible
@@ -377,52 +407,6 @@
}
@Test
- fun testChangeDensityOrFontScale() {
- val guts = spy(NotificationGuts(mContext))
- whenever(guts.post(any())).thenAnswer { invocation: InvocationOnMock ->
- handler.post(((invocation.arguments[0] as Runnable)))
- null
- }
-
- // Test doesn't support animation since the guts view is not attached.
- doNothing().whenever(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
-
- val realRow = createTestNotificationRow()
- val menuItem = createTestMenuItem(realRow)
-
- val row = spy(realRow)
-
- whenever(row.windowToken).thenReturn(Binder())
- whenever(row.guts).thenReturn(guts)
- doNothing().whenever(row).ensureGutsInflated()
-
- val realEntry = realRow.entry
- val entry = spy(realEntry)
-
- whenever(entry.row).thenReturn(row)
- whenever(entry.guts).thenReturn(guts)
-
- assertTrue(gutsManager.openGutsInternal(row, 0, 0, menuItem))
- executor.runAllReady()
- verify(guts).openControls(anyInt(), anyInt(), anyBoolean(), any<Runnable>())
-
- // called once by mGutsManager.bindGuts() in mGutsManager.openGuts()
- verify(row).setGutsView(any<MenuItem>())
-
- row.onDensityOrFontScaleChanged()
- gutsManager.onDensityOrFontScaleChanged(entry)
-
- executor.runAllReady()
-
- gutsManager.closeAndSaveGuts(false, false, false, 0, 0, false)
-
- verify(guts).closeControls(anyBoolean(), anyBoolean(), anyInt(), anyInt(), anyBoolean())
-
- // called again by mGutsManager.bindGuts(), in mGutsManager.onDensityOrFontScaleChanged()
- verify(row, times(2)).setGutsView(any<MenuItem>())
- }
-
- @Test
fun testAppOpsSettingsIntent_camera() {
val row = createTestNotificationRow()
val ops = ArraySet<Int>()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
index 6435e82..af67a04 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationSnoozeTest.java
@@ -16,29 +16,44 @@
package com.android.systemui.statusbar.notification.row;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import android.platform.test.annotations.EnableFlags;
import android.provider.Settings;
import android.testing.TestableResources;
-import android.util.KeyValueListParser;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.AnimatorTestRule;
+import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
+import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
@@ -46,8 +61,12 @@
public class NotificationSnoozeTest extends SysuiTestCase {
private static final int RES_DEFAULT = 2;
private static final int[] RES_OPTIONS = {1, 2, 3};
- private NotificationSnooze mNotificationSnooze;
- private KeyValueListParser mMockParser;
+ private final NotificationSwipeActionHelper mSnoozeListener = mock(
+ NotificationSwipeActionHelper.class);
+ private NotificationSnooze mUnderTest;
+
+ @Rule
+ public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
@Before
public void setUp() throws Exception {
@@ -56,62 +75,117 @@
TestableResources resources = mContext.getOrCreateTestableResources();
resources.addOverride(R.integer.config_notification_snooze_time_default, RES_DEFAULT);
resources.addOverride(R.array.config_notification_snooze_times, RES_OPTIONS);
- mNotificationSnooze = new NotificationSnooze(mContext, null);
- mMockParser = mock(KeyValueListParser.class);
+
+ mUnderTest = new NotificationSnooze(mContext, null);
+ mUnderTest.setSnoozeListener(mSnoozeListener);
+ mUnderTest.mExpandButton = mock(ImageView.class);
+ mUnderTest.mSnoozeView = mock(View.class);
+ mUnderTest.mSelectedOptionText = mock(TextView.class);
+ mUnderTest.mDivider = mock(View.class);
+ mUnderTest.mSnoozeOptionContainer = mock(ViewGroup.class);
+ mUnderTest.mSnoozeOptions = mock(List.class);
+ }
+
+ @After
+ public void tearDown() {
+ // Make sure all animations are finished
+ mAnimatorTestRule.advanceTimeBy(1000L);
}
@Test
- public void testGetOptionsWithNoConfig() throws Exception {
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ @EnableFlags(Flags.FLAG_NOTIFICATION_UNDO_GUTS_ON_CONFIG_CHANGED)
+ public void closeControls_withoutSave_performsUndo() {
+ ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+ mUnderTest.mSelectedOption = options.getFirst();
+ mUnderTest.showSnoozeOptions(true);
+
+ assertThat(
+ mUnderTest.handleCloseControls(/* save = */ false, /* force = */ false)).isFalse();
+
+ assertThat(mUnderTest.mSelectedOption).isNull();
+ assertThat(mUnderTest.isExpanded()).isFalse();
+ verify(mSnoozeListener, times(0)).snooze(any(), any());
+ }
+
+ @Test
+ public void closeControls_whenExpanded_collapsesOptions() {
+ ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+ mUnderTest.mSelectedOption = options.getFirst();
+ mUnderTest.showSnoozeOptions(true);
+
+ assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ false)).isTrue();
+
+ assertThat(mUnderTest.mSelectedOption).isNotNull();
+ assertThat(mUnderTest.isExpanded()).isFalse();
+ }
+
+ @Test
+ public void closeControls_whenCollapsed_commitsChanges() {
+ ArrayList<SnoozeOption> options = mUnderTest.getDefaultSnoozeOptions();
+ mUnderTest.mSelectedOption = options.getFirst();
+
+ assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ false)).isTrue();
+
+ verify(mSnoozeListener).snooze(any(), any());
+ }
+
+ @Test
+ public void closeControls_withForce_returnsFalse() {
+ assertThat(mUnderTest.handleCloseControls(/* save = */ true, /* force = */ true)).isFalse();
+ }
+
+ @Test
+ public void testGetOptionsWithNoConfig() {
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertEquals(3, result.size());
assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(2, result.get(1).getMinutesToSnoozeFor());
assertEquals(3, result.get(2).getMinutesToSnoozeFor());
- assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ assertEquals(2, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
}
@Test
- public void testGetOptionsWithInvalidConfig() throws Exception {
+ public void testGetOptionsWithInvalidConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"this is garbage");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertEquals(3, result.size());
assertEquals(1, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(2, result.get(1).getMinutesToSnoozeFor());
assertEquals(3, result.get(2).getMinutesToSnoozeFor());
- assertEquals(2, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ assertEquals(2, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
}
@Test
- public void testGetOptionsWithValidDefault() throws Exception {
+ public void testGetOptionsWithValidDefault() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"default=10,options_array=4:5:6:7");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
- assertNotNull(mNotificationSnooze.getDefaultOption()); // pick one
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
+ assertNotNull(mUnderTest.getDefaultOption()); // pick one
}
@Test
- public void testGetOptionsWithValidConfig() throws Exception {
+ public void testGetOptionsWithValidConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"default=6,options_array=4:5:6:7");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertEquals(4, result.size());
assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(5, result.get(1).getMinutesToSnoozeFor());
assertEquals(6, result.get(2).getMinutesToSnoozeFor());
assertEquals(7, result.get(3).getMinutesToSnoozeFor());
- assertEquals(6, mNotificationSnooze.getDefaultOption().getMinutesToSnoozeFor());
+ assertEquals(6, mUnderTest.getDefaultOption().getMinutesToSnoozeFor());
}
@Test
- public void testGetOptionsWithLongConfig() throws Exception {
+ public void testGetOptionsWithLongConfig() {
Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NOTIFICATION_SNOOZE_OPTIONS,
"default=6,options_array=4:5:6:7:8:9:10:11:12:13:14:15:16:17");
- ArrayList<SnoozeOption> result = mNotificationSnooze.getDefaultSnoozeOptions();
+ ArrayList<SnoozeOption> result = mUnderTest.getDefaultSnoozeOptions();
assertTrue(result.size() > 3);
assertEquals(4, result.get(0).getMinutesToSnoozeFor()); // respect order
assertEquals(5, result.get(1).getMinutesToSnoozeFor());
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index 09be93d..ea91b7a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -19,6 +19,7 @@
import android.content.Context
import android.content.res.Resources
import android.hardware.devicestate.DeviceStateManager
+import android.os.PowerManager.GO_TO_SLEEP_REASON_DEVICE_FOLD
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
@@ -27,16 +28,20 @@
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractorImpl
import com.android.systemui.defaultDeviceState
import com.android.systemui.deviceStateManager
-import com.android.systemui.display.data.repository.DeviceStateRepository
import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.FOLDED
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.HALF_FOLDED
+import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState.UNFOLDED
+import com.android.systemui.display.data.repository.fakeDeviceStateRepository
import com.android.systemui.foldedDeviceStateList
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.shared.model.ScreenPowerState
-import com.android.systemui.power.shared.model.WakeSleepReason
-import com.android.systemui.power.shared.model.WakefulnessModel
-import com.android.systemui.power.shared.model.WakefulnessState
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
+import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setScreenPowerState
+import com.android.systemui.power.domain.interactor.PowerInteractorFactory
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_OFF
+import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED
@@ -45,7 +50,7 @@
import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.unfoldedDeviceState
-import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
+import com.android.systemui.util.animation.data.repository.fakeAnimationStatusRepository
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.time.FakeSystemClock
@@ -77,14 +82,15 @@
private lateinit var displaySwitchLatencyTracker: DisplaySwitchLatencyTracker
@Captor private lateinit var loggerArgumentCaptor: ArgumentCaptor<DisplaySwitchLatencyEvent>
+ private val kosmos = Kosmos()
private val mockContext = mock<Context>()
private val resources = mock<Resources>()
- private val foldStateRepository = mock<DeviceStateRepository>()
- private val powerInteractor = mock<PowerInteractor>()
- private val animationStatusRepository = mock<AnimationStatusRepository>()
+ private val foldStateRepository = kosmos.fakeDeviceStateRepository
+ private val powerInteractor = PowerInteractorFactory.create().powerInteractor
+ private val animationStatusRepository = kosmos.fakeAnimationStatusRepository
private val keyguardInteractor = mock<KeyguardInteractor>()
private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>()
- private val kosmos = Kosmos()
+
private val deviceStateManager = kosmos.deviceStateManager
private val closedDeviceState = kosmos.foldedDeviceStateList.first()
private val openDeviceState = kosmos.unfoldedDeviceState
@@ -94,12 +100,7 @@
private val testDispatcher: TestDispatcher = StandardTestDispatcher()
private val testScope: TestScope = TestScope(testDispatcher)
- private val isAsleep = MutableStateFlow(false)
private val isAodAvailable = MutableStateFlow(false)
- private val deviceState = MutableStateFlow(DeviceState.UNFOLDED)
- private val screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_ON)
- private val areAnimationEnabled = MutableStateFlow(true)
- private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel())
private val systemClock = FakeSystemClock()
private val configurationController = FakeConfigurationController()
private val configurationRepository =
@@ -126,13 +127,10 @@
.thenReturn(listOf(closedDeviceState, openDeviceState))
whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(nonEmptyClosedDeviceStatesArray)
- whenever(foldStateRepository.state).thenReturn(deviceState)
- whenever(powerInteractor.isAsleep).thenReturn(isAsleep)
- whenever(animationStatusRepository.areAnimationsEnabled()).thenReturn(areAnimationEnabled)
- whenever(powerInteractor.screenPowerState).thenReturn(screenPowerState)
whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable)
- whenever(powerInteractor.detailedWakefulness).thenReturn(lastWakefulnessEvent)
-
+ animationStatusRepository.onAnimationStatusChanged(true)
+ powerInteractor.setAwakeForTest()
+ powerInteractor.setScreenPowerState(SCREEN_ON)
displaySwitchLatencyTracker =
DisplaySwitchLatencyTracker(
mockContext,
@@ -152,21 +150,19 @@
@Test
fun unfold_logsLatencyTillTransitionStarted() {
testScope.runTest {
- areAnimationEnabled.emit(true)
-
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
runCurrent()
systemClock.advanceTime(50)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
systemClock.advanceTime(200)
unfoldTransitionProgressProvider.onTransitionStarted()
runCurrent()
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
val loggedEvent = loggerArgumentCaptor.value
@@ -202,23 +198,22 @@
systemClock,
deviceStateManager,
)
- areAnimationEnabled.emit(true)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
systemClock.advanceTime(50)
runCurrent()
systemClock.advanceTime(200)
unfoldTransitionProgressProvider.onTransitionStarted()
runCurrent()
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
val loggedEvent = loggerArgumentCaptor.value
@@ -235,23 +230,23 @@
@Test
fun unfold_animationDisabled_logsLatencyTillScreenTurnedOn() {
testScope.runTest {
- areAnimationEnabled.emit(false)
+ animationStatusRepository.onAnimationStatusChanged(false)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
systemClock.advanceTime(50)
runCurrent()
unfoldTransitionProgressProvider.onTransitionStarted()
systemClock.advanceTime(200)
runCurrent()
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
val loggedEvent = loggerArgumentCaptor.value
@@ -268,19 +263,18 @@
@Test
fun foldWhileStayingAwake_logsLatency() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ setDeviceState(UNFOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
systemClock.advanceTime(200)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
runCurrent()
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -298,25 +292,19 @@
@Test
fun foldToAod_capturesToStateAsAod() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
isAodAvailable.emit(true)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- lastWakefulnessEvent.emit(
- WakefulnessModel(
- internalWakefulnessState = WakefulnessState.ASLEEP,
- lastSleepReason = WakeSleepReason.FOLD,
- )
- )
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
systemClock.advanceTime(200)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
runCurrent()
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -335,22 +323,21 @@
@Test
fun fold_notAFoldable_shouldNotLogLatency() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
whenever(resources.getIntArray(R.array.config_foldedDeviceStates))
.thenReturn(IntArray(0))
whenever(deviceStateManager.supportedDeviceStates)
.thenReturn(listOf(defaultDeviceState))
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
systemClock.advanceTime(200)
- screenPowerState.emit(ScreenPowerState.SCREEN_ON)
+ powerInteractor.setScreenPowerState(SCREEN_ON)
runCurrent()
verify(displaySwitchLatencyLogger, never()).log(any())
@@ -360,22 +347,16 @@
@Test
fun foldToScreenOff_capturesToStateAsScreenOff() {
testScope.runTest {
- areAnimationEnabled.emit(true)
- deviceState.emit(DeviceState.UNFOLDED)
+ setDeviceState(UNFOLDED)
isAodAvailable.emit(false)
displaySwitchLatencyTracker.start()
- deviceState.emit(DeviceState.HALF_FOLDED)
+ setDeviceState(HALF_FOLDED)
systemClock.advanceTime(50)
runCurrent()
- deviceState.emit(DeviceState.FOLDED)
- lastWakefulnessEvent.emit(
- WakefulnessModel(
- internalWakefulnessState = WakefulnessState.ASLEEP,
- lastSleepReason = WakeSleepReason.FOLD,
- )
- )
- screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ setDeviceState(FOLDED)
+ powerInteractor.setAsleepForTest(sleepReason = GO_TO_SLEEP_REASON_DEVICE_FOLD)
+ powerInteractor.setScreenPowerState(SCREEN_OFF)
runCurrent()
verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
@@ -390,4 +371,8 @@
assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
}
}
+
+ private suspend fun setDeviceState(state: DeviceState) {
+ foldStateRepository.emit(state)
+ }
}
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 5373b9d..fc9635b 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1365,6 +1365,8 @@
<string name="hub_onboarding_bottom_sheet_text">Access your favorite widgets and screen savers while charging.</string>
<!-- Hub onboarding bottom sheet action button title. [CHAR LIMIT=NONE] -->
<string name="hub_onboarding_bottom_sheet_action_button">Let\u2019s go</string>
+ <!-- Text for a tooltip that appears over the "show screensaver" button on glanceable hub. [CHAR LIMIT=NONE] -->
+ <string name="glanceable_hub_to_dream_button_tooltip">Show your favorite screensavers while charging</string>
<!-- Related to user switcher --><skip/>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
index b43ffc5..10b9303 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ILauncherProxy.aidl
@@ -146,9 +146,9 @@
void onUnbind(IRemoteCallback reply) = 35;
/**
- * Sent when {@link TaskbarDelegate#onDisplayReady} is called.
+ * Sent when {@link TaskbarDelegate#onDisplayAddSystemDecorations} is called.
*/
- void onDisplayReady(int displayId) = 36;
+ void onDisplayAddSystemDecorations(int displayId) = 36;
/**
* Sent when {@link TaskbarDelegate#onDisplayRemoved} is called.
diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index e76f38c..9507b04 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -62,15 +62,14 @@
scope,
mainDispatcher,
bgDispatcher,
- com.android.systemui.Flags.lockscreenCustomClocks()
+ com.android.systemui.shared.Flags.lockscreenCustomClocks()
|| featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
/* handleAllUsers= */ true,
new DefaultClockProvider(
context,
layoutInflater,
resources,
-
- com.android.systemui.Flags.clockReactiveVariants()
+ com.android.systemui.shared.Flags.clockReactiveVariants()
),
context.getString(R.string.lockscreen_clock_id_fallback),
clockBuffers,
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
index 1c99473..1264712 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractor.kt
@@ -32,6 +32,9 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
@@ -66,6 +69,7 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val repository: AuthenticationRepository,
private val selectedUserInteractor: SelectedUserInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) {
/**
* The currently-configured authentication method. This determines how the authentication
@@ -85,7 +89,11 @@
* `true` even when the lockscreen is showing and still needs to be dismissed by the user to
* proceed.
*/
- val authenticationMethod: Flow<AuthenticationMethodModel> = repository.authenticationMethod
+ val authenticationMethod: Flow<AuthenticationMethodModel> =
+ repository.authenticationMethod.logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ initialValue = AuthenticationMethodModel.None,
+ )
/**
* Whether the auto confirm feature is enabled for the currently-selected user.
diff --git a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
index 4e45fcc..744fd7e 100644
--- a/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/authentication/shared/model/AuthenticationMethodModel.kt
@@ -16,6 +16,9 @@
package com.android.systemui.authentication.shared.model
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
/** Enumerates all known authentication methods. */
sealed class AuthenticationMethodModel(
/**
@@ -24,8 +27,8 @@
* "Secure" authentication methods require authentication to unlock the device. Non-secure auth
* methods simply require user dismissal.
*/
- open val isSecure: Boolean,
-) {
+ open val isSecure: Boolean
+) : Diffable<AuthenticationMethodModel> {
/**
* Device doesn't use a secure authentication method. Either there is no lockscreen or the lock
* screen can be swiped away when displayed.
@@ -39,4 +42,8 @@
data object Pattern : AuthenticationMethodModel(isSecure = true)
data object Sim : AuthenticationMethodModel(isSecure = true)
+
+ override fun logDiffs(prevVal: AuthenticationMethodModel, row: TableRowLogger) {
+ row.logChange(columnName = "authenticationMethod", value = toString())
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
index 1923880..79e66a8 100644
--- a/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/data/CommonDataLayerModule.kt
@@ -16,6 +16,8 @@
package com.android.systemui.common.data
+import com.android.systemui.common.data.repository.BatteryRepository
+import com.android.systemui.common.data.repository.BatteryRepositoryImpl
import com.android.systemui.common.data.repository.PackageChangeRepository
import com.android.systemui.common.data.repository.PackageChangeRepositoryImpl
import dagger.Binds
@@ -27,4 +29,6 @@
abstract fun bindPackageChangeRepository(
impl: PackageChangeRepositoryImpl
): PackageChangeRepository
+
+ @Binds abstract fun bindBatteryRepository(impl: BatteryRepositoryImpl): BatteryRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt b/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt
new file mode 100644
index 0000000..63b0513
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/data/repository/BatteryRepository.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2025 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.common.data.repository
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.util.kotlin.isDevicePluggedIn
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.stateIn
+
+interface BatteryRepository {
+ val isDevicePluggedIn: Flow<Boolean>
+}
+
+@SysUISingleton
+class BatteryRepositoryImpl
+@Inject
+constructor(@Background bgScope: CoroutineScope, batteryController: BatteryController) :
+ BatteryRepository {
+
+ /** Returns {@code true} if the device is currently plugged in or wireless charging. */
+ override val isDevicePluggedIn: Flow<Boolean> =
+ batteryController
+ .isDevicePluggedIn()
+ .stateIn(bgScope, SharingStarted.WhileSubscribed(), batteryController.isPluggedIn)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
new file mode 100644
index 0000000..987776d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/domain/interactor/BatteryInteractor.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2025 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.common.domain.interactor
+
+import com.android.systemui.common.data.repository.BatteryRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class BatteryInteractor @Inject constructor(batteryRepository: BatteryRepository) {
+ val isDevicePluggedIn = batteryRepository.isDevicePluggedIn
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
index 47040fa..af8a5fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/DevicePosturingListener.kt
@@ -58,7 +58,6 @@
.distinctUntilChanged()
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "postured",
initialValue = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
index 882991aa..b89d322 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalMediaRepository.kt
@@ -52,7 +52,6 @@
override val mediaModel: Flow<CommunalMediaModel> =
_mediaModel.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
initialValue = CommunalMediaModel.INACTIVE,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
index 0902646..b747690 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalPrefsRepository.kt
@@ -56,6 +56,12 @@
/** Save the hub onboarding dismissed state for the current user. */
suspend fun setHubOnboardingDismissed(user: UserInfo)
+
+ /** Whether dream button tooltip has been dismissed. */
+ fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean>
+
+ /** Save the dream button tooltip dismissed state for the current user. */
+ suspend fun setDreamButtonTooltipDismissed(user: UserInfo)
}
@SysUISingleton
@@ -87,27 +93,34 @@
readKeyForUser(user, CTA_DISMISSED_STATE)
override suspend fun setCtaDismissed(user: UserInfo) =
- withContext(bgDispatcher) {
- getSharedPrefsForUser(user).edit().putBoolean(CTA_DISMISSED_STATE, true).apply()
- logger.i("Dismissed CTA tile")
- }
+ setBooleanKeyValueForUser(user, CTA_DISMISSED_STATE, "Dismissed CTA tile")
override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
readKeyForUser(user, HUB_ONBOARDING_DISMISSED_STATE)
override suspend fun setHubOnboardingDismissed(user: UserInfo) =
- withContext(bgDispatcher) {
- getSharedPrefsForUser(user)
- .edit()
- .putBoolean(HUB_ONBOARDING_DISMISSED_STATE, true)
- .apply()
- logger.i("Dismissed hub onboarding")
- }
+ setBooleanKeyValueForUser(user, HUB_ONBOARDING_DISMISSED_STATE, "Dismissed hub onboarding")
+
+ override fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean> =
+ readKeyForUser(user, DREAM_BUTTON_TOOLTIP_DISMISSED_STATE)
+
+ override suspend fun setDreamButtonTooltipDismissed(user: UserInfo) =
+ setBooleanKeyValueForUser(
+ user,
+ DREAM_BUTTON_TOOLTIP_DISMISSED_STATE,
+ "Dismissed dream button tooltip",
+ )
private fun getSharedPrefsForUser(user: UserInfo): SharedPreferences {
return userFileManager.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE, user.id)
}
+ private suspend fun setBooleanKeyValueForUser(user: UserInfo, key: String, logMsg: String) =
+ withContext(bgDispatcher) {
+ getSharedPrefsForUser(user).edit().putBoolean(key, true).apply()
+ logger.i(logMsg)
+ }
+
private fun readKeyForUser(user: UserInfo, key: String): Flow<Boolean> {
return backupRestorationEvents
.flatMapLatest {
@@ -122,5 +135,6 @@
const val FILE_NAME = "communal_hub_prefs"
const val CTA_DISMISSED_STATE = "cta_dismissed"
const val HUB_ONBOARDING_DISMISSED_STATE = "hub_onboarding_dismissed"
+ const val DREAM_BUTTON_TOOLTIP_DISMISSED_STATE = "dream_button_tooltip_dismissed_state"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 53122c5..abd1016 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -34,6 +34,7 @@
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryModule.Companion.DEFAULT_BACKGROUND_TYPE
import com.android.systemui.communal.shared.model.CommunalBackgroundType
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -60,6 +61,12 @@
fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
/**
+ * Returns a [WhenToDream] for the specified user, indicating what state the device should be in
+ * to trigger dreams.
+ */
+ fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream>
+
+ /**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
* This should be used for preventing basic glanceable hub functionality from running on devices
@@ -157,6 +164,49 @@
}
.flowOn(bgDispatcher)
+ override fun getWhenToDreamState(user: UserInfo): Flow<WhenToDream> =
+ secureSettings
+ .observerFlow(
+ userId = user.id,
+ names =
+ arrayOf(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ ),
+ )
+ .emitOnStart()
+ .map {
+ if (
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_SLEEP,
+ 0,
+ user.id,
+ ) == 1
+ ) {
+ WhenToDream.WHILE_CHARGING
+ } else if (
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK,
+ 0,
+ user.id,
+ ) == 1
+ ) {
+ WhenToDream.WHILE_DOCKED
+ } else if (
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ACTIVATE_ON_POSTURED,
+ 0,
+ user.id,
+ ) == 1
+ ) {
+ WhenToDream.WHILE_POSTURED
+ } else {
+ WhenToDream.NEVER
+ }
+ }
+ .flowOn(bgDispatcher)
+
override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
broadcastDispatcher
.broadcastFlow(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
index 19666e4..1b0a6a0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalTutorialRepository.kt
@@ -98,7 +98,6 @@
.filterNotNull()
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "tutorialSettingState",
initialValue = HUB_MODE_TUTORIAL_NOT_STARTED,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
index 74c335e..b4e6e93 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt
@@ -30,11 +30,13 @@
import com.android.systemui.Flags.communalResponsiveGrid
import com.android.systemui.Flags.glanceableHubBlurredBackground
import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.domain.interactor.BatteryInteractor
import com.android.systemui.communal.data.repository.CommunalMediaRepository
import com.android.systemui.communal.data.repository.CommunalSmartspaceRepository
import com.android.systemui.communal.data.repository.CommunalWidgetRepository
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.domain.model.CommunalContentModel.WidgetContent
+import com.android.systemui.communal.posturing.domain.interactor.PosturingInteractor
import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalContentSize
import com.android.systemui.communal.shared.model.CommunalContentSize.FixedSize.FULL
@@ -43,11 +45,14 @@
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.shared.model.EditModeState
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
import com.android.systemui.communal.widgets.WidgetConfigurator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dock.DockManager
+import com.android.systemui.dock.retrieveIsDocked
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.Edge
@@ -67,6 +72,7 @@
import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf
import com.android.systemui.util.kotlin.BooleanFlowOperators.not
import com.android.systemui.util.kotlin.emitOnStart
+import com.android.systemui.util.kotlin.isDevicePluggedIn
import javax.inject.Inject
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.CoroutineDispatcher
@@ -86,6 +92,7 @@
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -117,6 +124,9 @@
@CommunalLog logBuffer: LogBuffer,
@CommunalTableLog tableLogBuffer: TableLogBuffer,
private val managedProfileController: ManagedProfileController,
+ private val batteryInteractor: BatteryInteractor,
+ private val dockManager: DockManager,
+ private val posturingInteractor: PosturingInteractor,
) {
private val logger = Logger(logBuffer, "CommunalInteractor")
@@ -163,7 +173,6 @@
}
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isCommunalAvailable",
initialValue = false,
)
@@ -173,6 +182,33 @@
replay = 1,
)
+ /**
+ * Whether communal hub should be shown automatically, depending on the user's [WhenToDream]
+ * state.
+ */
+ val shouldShowCommunal: Flow<Boolean> =
+ allOf(
+ isCommunalAvailable,
+ communalSettingsInteractor.whenToDream
+ .flatMapLatest { whenToDream ->
+ when (whenToDream) {
+ WhenToDream.NEVER -> flowOf(false)
+
+ WhenToDream.WHILE_CHARGING -> batteryInteractor.isDevicePluggedIn
+
+ WhenToDream.WHILE_DOCKED ->
+ allOf(
+ batteryInteractor.isDevicePluggedIn,
+ dockManager.retrieveIsDocked(),
+ )
+
+ WhenToDream.WHILE_POSTURED ->
+ allOf(batteryInteractor.isDevicePluggedIn, posturingInteractor.postured)
+ }
+ }
+ .flowOn(bgDispatcher),
+ )
+
private val _isDisclaimerDismissed = MutableStateFlow(false)
val isDisclaimerDismissed: Flow<Boolean> = _isDisclaimerDismissed.asStateFlow()
@@ -300,7 +336,6 @@
}
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isCommunalShowing",
initialValue = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
index ec45d6c..cdf1703 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalPrefsInteractor.kt
@@ -49,7 +49,6 @@
.flatMapLatest { user -> repository.isCtaDismissed(user) }
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isCtaDismissed",
initialValue = false,
)
@@ -67,7 +66,6 @@
.flatMapLatest { user -> repository.isHubOnboardingDismissed(user) }
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isHubOnboardingDismissed",
initialValue = false,
)
@@ -80,6 +78,24 @@
fun setHubOnboardingDismissed(user: UserInfo = userTracker.userInfo) =
bgScope.launch { repository.setHubOnboardingDismissed(user) }
+ val isDreamButtonTooltipDismissed: Flow<Boolean> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.isDreamButtonTooltipDismissed(user) }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnPrefix = "",
+ columnName = "isDreamButtonTooltipDismissed",
+ initialValue = false,
+ )
+ .stateIn(
+ scope = bgScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
+ fun setDreamButtonTooltipDismissed(user: UserInfo = userTracker.userInfo) =
+ bgScope.launch { repository.setDreamButtonTooltipDismissed(user) }
+
private companion object {
const val TAG = "CommunalPrefsInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 1738f37..a0b1261 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.communal.data.model.CommunalEnabledState
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
import com.android.systemui.communal.shared.model.CommunalBackgroundType
+import com.android.systemui.communal.shared.model.WhenToDream
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.dagger.CommunalTableLog
@@ -73,6 +74,12 @@
repository.getScreensaverEnabledState(user)
}
+ /** When to dream for the currently selected user. */
+ val whenToDream: Flow<WhenToDream> =
+ userInteractor.selectedUserInfo.flatMapLatest { user ->
+ repository.getWhenToDreamState(user)
+ }
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
index 4e4ecc9..8f55d96 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalTutorialInteractor.kt
@@ -65,7 +65,6 @@
}
.logDiffsForTable(
tableLogBuffer = tableLogBuffer,
- columnPrefix = "",
columnName = "isTutorialAvailable",
initialValue = false,
)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
new file mode 100644
index 0000000..0d4eb60c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/WhenToDream.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2025 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.communal.shared.model
+
+enum class WhenToDream {
+ NEVER,
+ WHILE_CHARGING,
+ WHILE_DOCKED,
+ WHILE_POSTURED,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
index bbb1686..7e683c4 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -20,11 +20,14 @@
import android.app.DreamManager
import android.content.Intent
import android.provider.Settings
+import androidx.compose.runtime.getValue
import com.android.internal.logging.UiEventLogger
+import com.android.systemui.communal.domain.interactor.CommunalPrefsInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.kotlin.isDevicePluggedIn
@@ -37,7 +40,7 @@
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flowOn
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -47,18 +50,36 @@
constructor(
@Background private val backgroundContext: CoroutineContext,
batteryController: BatteryController,
+ private val prefsInteractor: CommunalPrefsInteractor,
private val settingsInteractor: CommunalSettingsInteractor,
private val activityStarter: ActivityStarter,
private val dreamManager: DreamManager,
private val uiEventLogger: UiEventLogger,
) : ExclusiveActivatable() {
+ private val hydrator = Hydrator("CommunalToDreamButtonViewModel.hydrator")
private val _requests = Channel<Unit>(Channel.BUFFERED)
/** Whether we should show a button on hub to switch to dream. */
- @SuppressLint("MissingPermission")
- val shouldShowDreamButtonOnHub =
- batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(backgroundContext)
+ val shouldShowDreamButtonOnHub: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "shouldShowDreamButtonOnHub",
+ initialValue = false,
+ source = batteryController.isDevicePluggedIn().distinctUntilChanged(),
+ )
+
+ /** Return whether the dream button tooltip has been dismissed. */
+ val shouldShowTooltip: Boolean by
+ hydrator.hydratedStateOf(
+ traceName = "shouldShowTooltip",
+ initialValue = false,
+ source = prefsInteractor.isDreamButtonTooltipDismissed.map { !it },
+ )
+
+ /** Set the dream button tooltip to be dismissed. */
+ fun setDreamButtonTooltipDismissed() {
+ prefsInteractor.setDreamButtonTooltipDismissed()
+ }
/** Handle a tap on the "show dream" button. */
fun onShowDreamButtonTap() {
@@ -86,6 +107,8 @@
}
}
+ launch { hydrator.activate() }
+
awaitCancellation()
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
index 1e7bec2..69da67e 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryBiometricAuthInteractor.kt
@@ -68,4 +68,10 @@
emptyFlow()
}
}
+
+ /** Triggered if a face failure occurs regardless of the mode. */
+ val faceFailure: Flow<FailedFaceAuthenticationStatus> =
+ deviceEntryFaceAuthInteractor.authenticationStatus.filterIsInstance<
+ FailedFaceAuthenticationStatus
+ >()
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
index cdd2b05..079d624 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryFaceAuthInteractor.kt
@@ -19,6 +19,7 @@
import com.android.systemui.CoreStartable
import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.log.table.TableLogBuffer
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -81,6 +82,8 @@
/** Whether face auth is considered class 3 */
fun isFaceAuthStrong(): Boolean
+
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer)
}
/**
@@ -93,17 +96,17 @@
*/
interface FaceAuthenticationListener {
/** Receive face isAuthenticated updates */
- fun onAuthenticatedChanged(isAuthenticated: Boolean)
+ fun onAuthenticatedChanged(isAuthenticated: Boolean) = Unit
/** Receive face authentication status updates */
- fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus)
+ fun onAuthenticationStatusChanged(status: FaceAuthenticationStatus) = Unit
/** Receive status updates whenever face detection runs */
- fun onDetectionStatusChanged(status: FaceDetectionStatus)
+ fun onDetectionStatusChanged(status: FaceDetectionStatus) = Unit
- fun onLockoutStateChanged(isLockedOut: Boolean)
+ fun onLockoutStateChanged(isLockedOut: Boolean) = Unit
- fun onRunningStateChanged(isRunning: Boolean)
+ fun onRunningStateChanged(isRunning: Boolean) = Unit
- fun onAuthEnrollmentStateChanged(enrolled: Boolean)
+ fun onAuthEnrollmentStateChanged(enrolled: Boolean) = Unit
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
index cd456a6..38e0503 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryHapticsInteractor.kt
@@ -123,7 +123,7 @@
private val playErrorHapticForBiometricFailure: Flow<Unit> =
merge(
deviceEntryFingerprintAuthInteractor.fingerprintFailure,
- deviceEntryBiometricAuthInteractor.faceOnlyFaceFailure,
+ deviceEntryBiometricAuthInteractor.faceFailure,
)
// map to Unit
.map {}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
index 4ddc98c..5b68597 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt
@@ -25,7 +25,10 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.data.repository.DeviceEntryRepository
import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.scene.data.model.asIterable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.Scenes
@@ -33,9 +36,11 @@
import com.android.systemui.utils.coroutines.flow.mapLatestConflated
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
@@ -43,6 +48,7 @@
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
/**
* Hosts application business logic related to device entry.
@@ -62,6 +68,7 @@
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val dismissCallbackRegistry: DismissCallbackRegistry,
sceneBackInteractor: SceneBackInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) {
/**
* Whether the device is unlocked.
@@ -147,6 +154,11 @@
) { enteredDirectly, enteredOnBackStack ->
enteredOnBackStack || enteredDirectly
}
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isDeviceEntered",
+ initialValue = false,
+ )
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -184,6 +196,11 @@
deviceUnlockStatus.deviceUnlockSource?.dismissesLockscreen == false)) &&
!isDeviceEntered
}
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "canSwipeToEnter",
+ initialValue = false,
+ )
.stateIn(
scope = applicationScope,
started = SharingStarted.Eagerly,
@@ -271,4 +288,29 @@
fun lockNow() {
deviceUnlockedInteractor.lockNow()
}
+
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ coroutineScope {
+ launch {
+ isDeviceEntered
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isDeviceEntered",
+ initialValue = isDeviceEntered.value,
+ )
+ .collect()
+ }
+
+ launch {
+ canSwipeToEnter
+ .map { it?.toString() ?: "" }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "canSwipeToEnter",
+ initialValue = canSwipeToEnter.value?.toString() ?: "",
+ )
+ .collect()
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
index 68aef52..b1be9a2 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractor.kt
@@ -33,8 +33,11 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.TrustInteractor
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.util.settings.repository.UserAwareSecureSettingsRepository
import com.android.systemui.utils.coroutines.flow.flatMapLatestConflated
@@ -48,6 +51,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
@@ -74,6 +78,7 @@
private val systemPropertiesHelper: SystemPropertiesHelper,
private val userAwareSecureSettingsRepository: UserAwareSecureSettingsRepository,
private val keyguardInteractor: KeyguardInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) : ExclusiveActivatable() {
private val deviceUnlockSource =
@@ -179,17 +184,33 @@
private val lockNowRequests = Channel<Unit>()
override suspend fun onActivated(): Nothing {
- authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
- if (!authMethod.isSecure) {
- // Device remains unlocked as long as the authentication method is not secure.
- Log.d(TAG, "remaining unlocked because auth method not secure")
- repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
- } else if (authMethod == AuthenticationMethodModel.Sim) {
- // Device remains locked while SIM is locked.
- Log.d(TAG, "remaining locked because SIM locked")
- repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
- } else {
- handleLockAndUnlockEvents()
+ coroutineScope {
+ launch {
+ authenticationInteractor.authenticationMethod.collectLatest { authMethod ->
+ if (!authMethod.isSecure) {
+ // Device remains unlocked as long as the authentication method is not
+ // secure.
+ Log.d(TAG, "remaining unlocked because auth method not secure")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(true, null)
+ } else if (authMethod == AuthenticationMethodModel.Sim) {
+ // Device remains locked while SIM is locked.
+ Log.d(TAG, "remaining locked because SIM locked")
+ repository.deviceUnlockStatus.value = DeviceUnlockStatus(false, null)
+ } else {
+ handleLockAndUnlockEvents()
+ }
+ }
+ }
+
+ launch {
+ deviceUnlockStatus
+ .map { it.isUnlocked }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isUnlocked",
+ initialValue = deviceUnlockStatus.value.isUnlocked,
+ )
+ .collect()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
index 9b8c2b1..ecc4dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/NoopDeviceEntryFaceAuthInteractor.kt
@@ -19,6 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.shared.model.FaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.FaceDetectionStatus
+import com.android.systemui.log.table.TableLogBuffer
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -73,4 +74,6 @@
override fun onWalletLaunched() = Unit
override fun onDeviceUnfolded() {}
+
+ override suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
index b19b2d9..4b90e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/SystemUIDeviceEntryFaceAuthInteractor.kt
@@ -44,6 +44,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.OFF
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -53,13 +55,16 @@
import com.android.systemui.user.data.repository.UserRepository
import com.android.systemui.util.kotlin.pairwise
import com.android.systemui.util.kotlin.sample
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
@@ -379,6 +384,27 @@
.launchIn(applicationScope)
}
+ override suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ conflatedCallbackFlow {
+ val listener =
+ object : FaceAuthenticationListener {
+ override fun onAuthEnrollmentStateChanged(enrolled: Boolean) {
+ trySend(isFaceAuthEnabledAndEnrolled())
+ }
+ }
+
+ registerListener(listener)
+
+ awaitClose { unregisterListener(listener) }
+ }
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isFaceAuthEnabledAndEnrolled",
+ initialValue = isFaceAuthEnabledAndEnrolled(),
+ )
+ .collect()
+ }
+
companion object {
const val TAG = "DeviceEntryFaceAuthInteractor"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index 8c60371..cf712f1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -19,8 +19,12 @@
import android.animation.ValueAnimator
import android.util.MathUtils
import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.systemui.Flags.communalSceneKtfRefactor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
@@ -39,6 +43,10 @@
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
+import java.util.UUID
+import javax.inject.Inject
+import kotlin.time.Duration.Companion.milliseconds
+import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -47,11 +55,6 @@
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
-import kotlin.time.Duration.Companion.milliseconds
-import kotlin.time.Duration.Companion.seconds
-import java.util.UUID
-import javax.inject.Inject
-import com.android.app.tracing.coroutines.launchTraced as launch
@SysUISingleton
class FromLockscreenTransitionInteractor
@@ -68,6 +71,8 @@
powerInteractor: PowerInteractor,
private val glanceableHubTransitions: GlanceableHubTransitions,
private val communalSettingsInteractor: CommunalSettingsInteractor,
+ private val communalInteractor: CommunalInteractor,
+ private val communalSceneInteractor: CommunalSceneInteractor,
private val swipeToDismissInteractor: SwipeToDismissInteractor,
keyguardOcclusionInteractor: KeyguardOcclusionInteractor,
) :
@@ -94,6 +99,9 @@
if (!communalSceneKtfRefactor()) {
listenForLockscreenToGlanceableHub()
}
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ listenForLockscreenToGlanceableHubV2()
+ }
}
/**
@@ -268,9 +276,7 @@
it.transitionState == TransitionState.CANCELED &&
it.to == KeyguardState.PRIMARY_BOUNCER
}
- .collect {
- transitionId = null
- }
+ .collect { transitionId = null }
}
}
@@ -370,6 +376,19 @@
}
}
+ private fun listenForLockscreenToGlanceableHubV2() {
+ scope.launch {
+ communalInteractor.shouldShowCommunal
+ .filterRelevantKeyguardStateAnd { shouldShow -> shouldShow }
+ .collect {
+ communalSceneInteractor.changeScene(
+ newScene = CommunalScenes.Communal,
+ loggingReason = "lockscreen to communal",
+ )
+ }
+ }
+ }
+
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
interpolator = Interpolators.LINEAR
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
index 42cbd7d..a1f288e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardEnabledInteractor.kt
@@ -24,6 +24,8 @@
import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.kotlin.sample
@@ -32,6 +34,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
@@ -166,4 +169,14 @@
isKeyguardEnabled.value && lockPatternUtils.isLockScreenDisabled(userId)
}
}
+
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ isKeyguardEnabled
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isKeyguardEnabled",
+ initialValue = isKeyguardEnabled.value,
+ )
+ .collect()
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 75178f0..3739d17 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -42,6 +42,8 @@
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
import com.android.systemui.keyguard.shared.model.StatusBarState
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.res.R
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -60,6 +62,7 @@
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combineTransform
import kotlinx.coroutines.flow.debounce
@@ -533,6 +536,16 @@
repository.setNotificationStackAbsoluteBottom(bottom)
}
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ isDozing
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ columnName = "isDozing",
+ initialValue = isDozing.value,
+ )
+ .collect()
+ }
+
companion object {
private const val TAG = "KeyguardInteractor"
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index de5088c..898b68d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -475,7 +475,7 @@
KeyguardPickerFlag(
name = Contract.FlagsTable.FLAG_NAME_CUSTOM_CLOCKS_ENABLED,
value =
- com.android.systemui.Flags.lockscreenCustomClocks() ||
+ com.android.systemui.shared.Flags.lockscreenCustomClocks() ||
featureFlags.isEnabled(Flags.LOCKSCREEN_CUSTOM_CLOCKS),
),
KeyguardPickerFlag(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
index 70a52af..017fe16 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt
@@ -56,6 +56,7 @@
import com.android.systemui.keyguard.domain.interactor.WallpaperFocalAreaInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.ui.view.layout.sections.AodPromotedNotificationSection
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBlueprintViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
@@ -184,6 +185,7 @@
viewModel.translationY.collect { y ->
childViews[burnInLayerId]?.translationY = y
childViews[largeClockId]?.translationY = y
+ childViews[aodPromotedNotificationId]?.translationY = y
childViews[aodNotificationIconContainerId]?.translationY = y
}
}
@@ -195,6 +197,7 @@
state.isToOrFrom(KeyguardState.AOD) -> {
// Large Clock is not translated in the x direction
childViews[burnInLayerId]?.translationX = px
+ childViews[aodPromotedNotificationId]?.translationX = px
childViews[aodNotificationIconContainerId]?.translationX = px
}
state.isToOrFrom(KeyguardState.GLANCEABLE_HUB) -> {
@@ -291,11 +294,17 @@
blueprintViewModel.refreshBlueprint()
}
childViews[aodNotificationIconContainerId]
- ?.setAodNotifIconContainerIsVisible(
- isVisible,
- iconsAppearTranslationPx.value,
- screenOffAnimationController,
- )
+ ?.setAodNotifIconContainerIsVisible(isVisible)
+ }
+ }
+
+ launch {
+ viewModel.isNotifIconContainerVisible.collect { isVisible ->
+ if (isVisible.value) {
+ blueprintViewModel.refreshBlueprint()
+ }
+ childViews[aodPromotedNotificationId]
+ ?.setAodNotifIconContainerIsVisible(isVisible)
}
}
@@ -524,11 +533,7 @@
}
}
- private fun View.setAodNotifIconContainerIsVisible(
- isVisible: AnimatedValue<Boolean>,
- iconsAppearTranslationPx: Int,
- screenOffAnimationController: ScreenOffAnimationController,
- ) {
+ private fun View.setAodNotifIconContainerIsVisible(isVisible: AnimatedValue<Boolean>) {
animate().cancel()
val animatorListener =
object : AnimatorListenerAdapter() {
@@ -563,6 +568,7 @@
}
private val burnInLayerId = R.id.burn_in_layer
+ private val aodPromotedNotificationId = AodPromotedNotificationSection.viewId
private val aodNotificationIconContainerId = R.id.aod_notification_icon_container
private val largeClockId = customR.id.lockscreen_clock_view_large
private val smallClockId = customR.id.lockscreen_clock_view
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
index 454ba9a..d280862 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/InWindowLauncherUnlockAnimationManager.kt
@@ -19,6 +19,7 @@
package com.android.systemui.keyguard.ui.view
import android.graphics.Rect
+import android.os.DeadObjectException
import android.util.Log
import android.view.View
import com.android.systemui.dagger.SysUISingleton
@@ -192,7 +193,12 @@
launcherAnimationController?.let {
manualUnlockAmount = amount
- it.setUnlockAmount(amount, forceIfAnimating)
+
+ try {
+ it.setUnlockAmount(amount, forceIfAnimating)
+ } catch (e: DeadObjectException) {
+ Log.e(TAG, "DeadObjectException in setUnlockAmount($amount, $forceIfAnimating)", e)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
index f0c924f..11a509a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt
@@ -307,6 +307,16 @@
BurnInScaleViewModel(scale = it.scale, scaleClockOnly = it.scaleClockOnly)
}
+ val isAodPromotedNotifVisible: StateFlow<Boolean> =
+ keyguardTransitionInteractor
+ .transitionValue(AOD)
+ .map { it == 1f }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false,
+ )
+
/** Is the notification icon container visible? */
val isNotifIconContainerVisible: StateFlow<AnimatedValue<Boolean>> =
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
index ebda376..babb640 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarControllerImpl.java
@@ -284,7 +284,10 @@
}
@Override
- public void onDisplayReady(int displayId) {
+ public void onDisplayAddSystemDecorations(int displayId) {
+ if (enableDisplayContentModeManagement()) {
+ mHasNavBar.put(displayId, true);
+ }
Display display = mDisplayManager.getDisplay(displayId);
mIsLargeScreen = isLargeScreen(mContext);
createNavigationBar(display, null /* savedState */, null /* result */);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index 9d89430..c4d847f 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -238,16 +238,16 @@
}
@Override
- public void onDisplayReady(int displayId) {
- CommandQueue.Callbacks.super.onDisplayReady(displayId);
+ public void onDisplayAddSystemDecorations(int displayId) {
+ CommandQueue.Callbacks.super.onDisplayAddSystemDecorations(displayId);
if (mLauncherProxyService.getProxy() == null) {
return;
}
try {
- mLauncherProxyService.getProxy().onDisplayReady(displayId);
+ mLauncherProxyService.getProxy().onDisplayAddSystemDecorations(displayId);
} catch (RemoteException e) {
- Log.e(TAG, "onDisplayReady() failed", e);
+ Log.e(TAG, "onDisplayAddSystemDecorations() failed", e);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index f15a7b3..f8d442d 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -22,6 +22,8 @@
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.classifier.FalsingCollectorActual
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.power.data.repository.PowerRepository
import com.android.systemui.power.shared.model.DozeScreenStateModel
@@ -35,6 +37,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
@@ -228,6 +231,15 @@
repository.updateWakefulness(powerButtonLaunchGestureTriggered = true)
}
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ detailedWakefulness
+ .logDiffsForTable(
+ tableLogBuffer = tableLogBuffer,
+ initialValue = detailedWakefulness.value,
+ )
+ .collect()
+ }
+
companion object {
private const val FSI_WAKE_WHY = "full_screen_intent"
diff --git a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
index 0f49c94..297c6af 100644
--- a/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/shared/model/WakefulnessModel.kt
@@ -1,6 +1,8 @@
package com.android.systemui.power.shared.model
import com.android.systemui.keyguard.KeyguardService
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
/**
* Models whether the device is awake or asleep, along with information about why we're in that
@@ -35,7 +37,7 @@
* to a subsequent power gesture.
*/
val powerButtonLaunchGestureTriggered: Boolean = false,
-) {
+) : Diffable<WakefulnessModel> {
fun isAwake() =
internalWakefulnessState == WakefulnessState.AWAKE ||
internalWakefulnessState == WakefulnessState.STARTING_TO_WAKE
@@ -58,4 +60,8 @@
return isAwake() &&
(lastWakeReason == WakeSleepReason.TAP || lastWakeReason == WakeSleepReason.GESTURE)
}
+
+ override fun logDiffs(prevVal: WakefulnessModel, row: TableRowLogger) {
+ row.logChange(columnName = "wakefulness", value = toString())
+ }
}
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 07de466..85b677b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt
@@ -32,11 +32,7 @@
import androidx.activity.OnBackPressedDispatcherOwner
import androidx.activity.setViewTreeOnBackPressedDispatcherOwner
import androidx.annotation.VisibleForTesting
-import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.core.tween
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement.spacedBy
import androidx.compose.foundation.layout.Box
@@ -120,6 +116,7 @@
import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams
import com.android.systemui.qs.composefragment.ui.notificationScrimClip
import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings
+import com.android.systemui.qs.composefragment.ui.toEditMode
import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel
import com.android.systemui.qs.flags.QSComposeFragment
import com.android.systemui.qs.footer.ui.compose.FooterActions
@@ -144,6 +141,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collectLatest
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -273,36 +271,7 @@
// by the composables.
.gesturesDisabled(viewModel.showingMirror)
) {
- val isEditing by
- viewModel.containerViewModel.editModeViewModel.isEditing
- .collectAsStateWithLifecycle()
- val animationSpecEditMode = tween<Float>(EDIT_MODE_TIME_MILLIS)
- AnimatedContent(
- targetState = isEditing,
- transitionSpec = {
- fadeIn(animationSpecEditMode) togetherWith
- fadeOut(animationSpecEditMode)
- },
- label = "EditModeAnimatedContent",
- ) { editing ->
- if (editing) {
- val qqsPadding = viewModel.qqsHeaderHeight
- EditMode(
- viewModel = viewModel.containerViewModel.editModeViewModel,
- modifier =
- Modifier.fillMaxWidth()
- .padding(top = { qqsPadding })
- .padding(
- horizontal = {
- QuickSettingsShade.Dimensions.Padding
- .roundToPx()
- }
- ),
- )
- } else {
- CollapsableQuickSettingsSTL()
- }
- }
+ CollapsableQuickSettingsSTL()
}
}
}
@@ -324,12 +293,17 @@
from(QuickQuickSettings, QuickSettings) {
quickQuickSettingsToQuickSettings(viewModel::animateTilesExpansion::get)
}
+ to(SceneKeys.EditMode) {
+ spec = tween(durationMillis = EDIT_MODE_TIME_MILLIS)
+ toEditMode()
+ }
},
)
LaunchedEffect(Unit) {
synchronizeQsState(
sceneState,
+ viewModel.containerViewModel.editModeViewModel.isEditing,
snapshotFlow { viewModel.expansionState }.map { it.progress },
)
}
@@ -337,12 +311,20 @@
SceneTransitionLayout(state = sceneState, modifier = Modifier.fillMaxSize()) {
scene(QuickSettings) {
LaunchedEffect(Unit) { viewModel.onQSOpen() }
- QuickSettingsElement()
+ QuickSettingsElement(Modifier.element(QuickSettings.rootElementKey))
}
scene(QuickQuickSettings) {
LaunchedEffect(Unit) { viewModel.onQQSOpen() }
- QuickQuickSettingsElement()
+ // Cannot pass the element modifier in because the top element has a `testTag`
+ // and this would overwrite it.
+ Box(Modifier.element(QuickQuickSettings.rootElementKey)) {
+ QuickQuickSettingsElement()
+ }
+ }
+
+ scene(SceneKeys.EditMode) {
+ EditModeElement(Modifier.element(SceneKeys.EditMode.rootElementKey))
}
}
}
@@ -582,7 +564,7 @@
}
@Composable
- private fun ContentScope.QuickQuickSettingsElement() {
+ private fun ContentScope.QuickQuickSettingsElement(modifier: Modifier = Modifier) {
val qqsPadding = viewModel.qqsHeaderHeight
val bottomPadding = viewModel.qqsBottomPadding
DisposableEffect(Unit) {
@@ -595,7 +577,7 @@
.squishiness
.collectAsStateWithLifecycle()
- Column(modifier = Modifier.sysuiResTag(ResIdTags.quickQsPanel)) {
+ Column(modifier = modifier.sysuiResTag(ResIdTags.quickQsPanel)) {
Box(
modifier =
Modifier.fillMaxWidth()
@@ -666,12 +648,12 @@
}
@Composable
- private fun ContentScope.QuickSettingsElement() {
+ private fun ContentScope.QuickSettingsElement(modifier: Modifier = Modifier) {
val qqsPadding = viewModel.qqsHeaderHeight
val qsExtraPadding = dimensionResource(R.dimen.qs_panel_padding_top)
Column(
modifier =
- Modifier.collapseExpandSemanticAction(
+ modifier.collapseExpandSemanticAction(
stringResource(id = R.string.accessibility_quick_settings_collapse)
)
) {
@@ -776,6 +758,18 @@
}
}
+ @Composable
+ private fun EditModeElement(modifier: Modifier = Modifier) {
+ // No need for top padding, the Scaffold inside takes care of the correct insets
+ EditMode(
+ viewModel = viewModel.containerViewModel.editModeViewModel,
+ modifier =
+ modifier
+ .fillMaxWidth()
+ .padding(horizontal = { QuickSettingsShade.Dimensions.Padding.roundToPx() }),
+ )
+ }
+
private fun Modifier.collapseExpandSemanticAction(label: String): Modifier {
return viewModel.collapseExpandAccessibilityAction?.let {
semantics {
@@ -863,6 +857,7 @@
object SceneKeys {
val QuickQuickSettings = SceneKey("QuickQuickSettingsScene")
val QuickSettings = SceneKey("QuickSettingsScene")
+ val EditMode = SceneKey("EditModeScene")
fun QSFragmentComposeViewModel.QSExpansionState.toIdleSceneKey(): SceneKey {
return when {
@@ -880,7 +875,11 @@
}
}
-suspend fun synchronizeQsState(state: MutableSceneTransitionLayoutState, expansion: Flow<Float>) {
+private suspend fun synchronizeQsState(
+ state: MutableSceneTransitionLayoutState,
+ editMode: Flow<Boolean>,
+ expansion: Flow<Float>,
+) {
coroutineScope {
val animationScope = this
@@ -891,23 +890,30 @@
currentTransition = null
}
- expansion.collectLatest { progress ->
- when (progress) {
- 0f -> snapTo(QuickQuickSettings)
- 1f -> snapTo(QuickSettings)
- else -> {
- val transition = currentTransition
- if (transition != null) {
- transition.progress = progress
- return@collectLatest
- }
+ editMode.combine(expansion, ::Pair).collectLatest { (editMode, progress) ->
+ if (editMode && state.currentScene != SceneKeys.EditMode) {
+ state.setTargetScene(SceneKeys.EditMode, animationScope)?.second?.join()
+ } else if (!editMode && state.currentScene == SceneKeys.EditMode) {
+ state.setTargetScene(SceneKeys.QuickSettings, animationScope)?.second?.join()
+ }
+ if (!editMode) {
+ when (progress) {
+ 0f -> snapTo(QuickQuickSettings)
+ 1f -> snapTo(QuickSettings)
+ else -> {
+ val transition = currentTransition
+ if (transition != null) {
+ transition.progress = progress
+ return@collectLatest
+ }
- val newTransition =
- ExpansionTransition(progress).also { currentTransition = it }
- state.startTransitionImmediately(
- animationScope = animationScope,
- transition = newTransition,
- )
+ val newTransition =
+ ExpansionTransition(progress).also { currentTransition = it }
+ state.startTransitionImmediately(
+ animationScope = animationScope,
+ transition = newTransition,
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt
new file mode 100644
index 0000000..0c6f3ee
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/ToEditMode.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2025 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.composefragment.ui
+
+import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.qs.composefragment.SceneKeys
+
+fun TransitionBuilder.toEditMode() {
+ fractionRange(start = 0.5f) { fade(SceneKeys.EditMode.rootElementKey) }
+ fractionRange(end = 0.5f) {
+ fade(SceneKeys.QuickQuickSettings.rootElementKey)
+ fade(SceneKeys.QuickSettings.rootElementKey)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
index be792df..f2f237a 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/SceneDomainModule.kt
@@ -16,13 +16,27 @@
package com.android.systemui.scene.domain
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.scene.domain.resolver.SceneResolverModule
import dagger.Module
+import dagger.Provides
+import javax.inject.Qualifier
-@Module(
- includes =
- [
- SceneResolverModule::class,
- ]
-)
-object SceneDomainModule
+@Module(includes = [SceneResolverModule::class])
+object SceneDomainModule {
+
+ @JvmStatic
+ @Provides
+ @SysUISingleton
+ @SceneFrameworkTableLog
+ fun provideSceneFrameworkTableLogBuffer(factory: TableLogBufferFactory): TableLogBuffer {
+ return factory.create("SceneFrameworkTableLog", 100)
+ }
+}
+
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class SceneFrameworkTableLog
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
index bebd398..c9d8e02 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneBackInteractor.kt
@@ -18,11 +18,16 @@
import com.android.compose.animation.scene.SceneKey
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.scene.data.model.SceneStack
+import com.android.systemui.scene.data.model.asIterable
import com.android.systemui.scene.data.model.peek
import com.android.systemui.scene.data.model.pop
import com.android.systemui.scene.data.model.push
import com.android.systemui.scene.data.model.sceneStackOf
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneContainerConfig
import javax.inject.Inject
@@ -39,6 +44,7 @@
constructor(
private val logger: SceneLogger,
private val sceneContainerConfig: SceneContainerConfig,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) {
private val _backStack = MutableStateFlow(sceneStackOf())
val backStack: StateFlow<SceneStack> = _backStack.asStateFlow()
@@ -58,6 +64,7 @@
fun onSceneChange(from: SceneKey, to: SceneKey) {
check(from != to) { "from == to, from=${from.debugName}, to=${to.debugName}" }
+ val prevVal = backStack.value
_backStack.update { stack ->
when (stackOperation(from, to, stack)) {
null -> stack
@@ -68,12 +75,21 @@
}
}
logger.logSceneBackStack(backStack.value)
+ tableLogBuffer.logDiffs(
+ prevVal = DiffableSceneStack(prevVal),
+ newVal = DiffableSceneStack(backStack.value),
+ )
}
/** Applies the given [transform] to the back stack. */
fun updateBackStack(transform: (SceneStack) -> SceneStack) {
+ val prevVal = backStack.value
_backStack.update { stack -> transform(stack) }
logger.logSceneBackStack(backStack.value)
+ tableLogBuffer.logDiffs(
+ prevVal = DiffableSceneStack(prevVal),
+ newVal = DiffableSceneStack(backStack.value),
+ )
}
private fun stackOperation(from: SceneKey, to: SceneKey, stack: SceneStack): StackOperation? {
@@ -106,4 +122,15 @@
private data object Push : StackOperation
private data object Pop : StackOperation
+
+ private class DiffableSceneStack(private val sceneStack: SceneStack) :
+ Diffable<DiffableSceneStack> {
+
+ override fun logDiffs(prevVal: DiffableSceneStack, row: TableRowLogger) {
+ row.logChange(
+ columnName = "backStack",
+ value = sceneStack.asIterable().joinToString { it.debugName },
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 8bc9d96..9c04323 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -27,14 +27,19 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.TableRowLogger
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.resolver.SceneResolver
import com.android.systemui.scene.shared.logger.SceneLogger
import com.android.systemui.scene.shared.model.SceneFamilies
import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.util.kotlin.pairwise
import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -47,6 +52,7 @@
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
+import kotlinx.coroutines.launch
/**
* Generic business logic and app state accessors for the scene framework.
@@ -562,6 +568,28 @@
decrementActiveTransitionAnimationCount()
}
+ suspend fun hydrateTableLogBuffer(tableLogBuffer: TableLogBuffer) {
+ coroutineScope {
+ launch {
+ currentScene
+ .map { sceneKey -> DiffableSceneKey(key = sceneKey) }
+ .pairwise()
+ .collect { (prev, current) ->
+ tableLogBuffer.logDiffs(prevVal = prev, newVal = current)
+ }
+ }
+
+ launch {
+ currentOverlays
+ .map { overlayKeys -> DiffableOverlayKeys(keys = overlayKeys) }
+ .pairwise()
+ .collect { (prev, current) ->
+ tableLogBuffer.logDiffs(prevVal = prev, newVal = current)
+ }
+ }
+ }
+ }
+
private fun decrementActiveTransitionAnimationCount() {
repository.activeTransitionAnimationCount.update { current ->
(current - 1).also {
@@ -573,4 +601,20 @@
}
}
}
+
+ private class DiffableSceneKey(private val key: SceneKey) : Diffable<DiffableSceneKey> {
+ override fun logDiffs(prevVal: DiffableSceneKey, row: TableRowLogger) {
+ row.logChange(columnName = "currentScene", value = key.debugName)
+ }
+ }
+
+ private class DiffableOverlayKeys(private val keys: Set<OverlayKey>) :
+ Diffable<DiffableOverlayKeys> {
+ override fun logDiffs(prevVal: DiffableOverlayKeys, row: TableRowLogger) {
+ row.logChange(
+ columnName = "currentOverlays",
+ value = keys.joinToString { key -> key.debugName },
+ )
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index 8602884..2fd5841 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardEnabledInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor.Companion.keyguardScenes
+import com.android.systemui.log.table.TableLogBuffer
import com.android.systemui.model.SceneContainerPlugin
import com.android.systemui.model.SysUiState
import com.android.systemui.model.updateFlags
@@ -54,6 +55,7 @@
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.scene.data.model.asIterable
import com.android.systemui.scene.data.model.sceneStackOf
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.scene.domain.interactor.DisabledContentInteractor
import com.android.systemui.scene.domain.interactor.SceneBackInteractor
import com.android.systemui.scene.domain.interactor.SceneContainerOcclusionInteractor
@@ -145,6 +147,7 @@
private val disabledContentInteractor: DisabledContentInteractor,
private val activityTransitionAnimator: ActivityTransitionAnimator,
private val shadeModeInteractor: ShadeModeInteractor,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
@@ -154,6 +157,7 @@
override fun start() {
if (SceneContainerFlag.isEnabled) {
sceneLogger.logFrameworkEnabled(isEnabled = true)
+ applicationScope.launch { hydrateTableLogBuffer() }
hydrateVisibility()
automaticallySwitchScenes()
hydrateSystemUiState()
@@ -224,6 +228,16 @@
}
}
+ private suspend fun hydrateTableLogBuffer() {
+ coroutineScope {
+ launch { sceneInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { keyguardEnabledInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { faceUnlockInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { powerInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ launch { keyguardInteractor.hydrateTableLogBuffer(tableLogBuffer) }
+ }
+ }
+
private fun resetShadeSessions() {
applicationScope.launch {
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 6844f05..eae0ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -16,6 +16,8 @@
package com.android.systemui.settings.brightness;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.EXTRA_BRIGHTNESS_DIALOG_IS_FULL_WIDTH;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
@@ -157,6 +159,7 @@
}
void setBrightnessDialogViewAttributes(View container) {
+ Configuration configuration = getResources().getConfiguration();
// The brightness mirror container is INVISIBLE by default.
container.setVisibility(View.VISIBLE);
ViewGroup.MarginLayoutParams lp =
@@ -171,9 +174,16 @@
R.dimen.notification_guts_option_vertical_padding);
lp.topMargin = verticalMargin;
+ // If in multi-window or freeform, increase the top margin so the brightness dialog
+ // doesn't get cut off.
+ final int windowingMode = configuration.windowConfiguration.getWindowingMode();
+ if (windowingMode == WINDOWING_MODE_MULTI_WINDOW
+ || windowingMode == WINDOWING_MODE_FREEFORM) {
+ lp.topMargin += 50;
+ }
+
lp.bottomMargin = verticalMargin;
- Configuration configuration = getResources().getConfiguration();
int orientation = configuration.orientation;
int windowWidth = getWindowAvailableWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
index 59d8124..0145150 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractor.kt
@@ -19,6 +19,9 @@
import android.provider.Settings
import androidx.annotation.FloatRange
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.log.table.TableLogBuffer
+import com.android.systemui.log.table.logDiffsForTable
+import com.android.systemui.scene.domain.SceneFrameworkTableLog
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.shade.shared.flag.DualShade
import com.android.systemui.shade.shared.model.ShadeMode
@@ -81,8 +84,9 @@
@Inject
constructor(
@Application applicationScope: CoroutineScope,
- repository: ShadeRepository,
+ private val repository: ShadeRepository,
secureSettingsRepository: SecureSettingsRepository,
+ @SceneFrameworkTableLog private val tableLogBuffer: TableLogBuffer,
) : ShadeModeInteractor {
private val isDualShadeEnabled: Flow<Boolean> =
@@ -93,17 +97,17 @@
override val isShadeLayoutWide: StateFlow<Boolean> = repository.isShadeLayoutWide
+ private val shadeModeInitialValue: ShadeMode
+ get() =
+ determineShadeMode(
+ isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
+ isShadeLayoutWide = repository.isShadeLayoutWide.value,
+ )
+
override val shadeMode: StateFlow<ShadeMode> =
combine(isDualShadeEnabled, repository.isShadeLayoutWide, ::determineShadeMode)
- .stateIn(
- applicationScope,
- SharingStarted.Eagerly,
- initialValue =
- determineShadeMode(
- isDualShadeEnabled = DUAL_SHADE_ENABLED_DEFAULT,
- isShadeLayoutWide = repository.isShadeLayoutWide.value,
- ),
- )
+ .logDiffsForTable(tableLogBuffer = tableLogBuffer, initialValue = shadeModeInitialValue)
+ .stateIn(applicationScope, SharingStarted.Eagerly, initialValue = shadeModeInitialValue)
@FloatRange(from = 0.0, to = 1.0) override fun getTopEdgeSplitFraction(): Float = 0.5f
diff --git a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
index a8199a4..8b3ce0f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/shared/model/ShadeMode.kt
@@ -16,15 +16,18 @@
package com.android.systemui.shade.shared.model
+import com.android.systemui.log.table.Diffable
+import com.android.systemui.log.table.TableRowLogger
+
/** Enumerates all known modes of operation of the shade. */
-sealed interface ShadeMode {
+sealed class ShadeMode : Diffable<ShadeMode> {
/**
* The single or "accordion" shade where the QS and notification parts are in two vertically
* stacked panels and the user can swipe up and down to expand or collapse between the two
* parts.
*/
- data object Single : ShadeMode
+ data object Single : ShadeMode()
/**
* The split shade where, on large screens and unfolded foldables, the QS and notification parts
@@ -32,14 +35,18 @@
*
* Note: This isn't the only mode where the shade is wide.
*/
- data object Split : ShadeMode
+ data object Split : ShadeMode()
/**
* The dual shade where the QS and notification parts each have their own independently
* expandable/collapsible panel on either side of the large screen / unfolded device or sharing
* a space on a small screen or folded device.
*/
- data object Dual : ShadeMode
+ data object Dual : ShadeMode()
+
+ override fun logDiffs(prevVal: ShadeMode, row: TableRowLogger) {
+ row.logChange("shadeMode", toString())
+ }
companion object {
@JvmStatic fun dual(): Dual = Dual
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index dcea8d8..1720898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -111,7 +111,7 @@
private static final int MSG_COLLAPSE_PANELS = 4 << MSG_SHIFT;
private static final int MSG_EXPAND_SETTINGS = 5 << MSG_SHIFT;
private static final int MSG_SYSTEM_BAR_CHANGED = 6 << MSG_SHIFT;
- private static final int MSG_DISPLAY_READY = 7 << MSG_SHIFT;
+ private static final int MSG_DISPLAY_ADD_SYSTEM_DECORATIONS = 7 << MSG_SHIFT;
private static final int MSG_SHOW_IME_BUTTON = 8 << MSG_SHIFT;
private static final int MSG_TOGGLE_RECENT_APPS = 9 << MSG_SHIFT;
private static final int MSG_PRELOAD_RECENT_APPS = 10 << MSG_SHIFT;
@@ -415,9 +415,9 @@
}
/**
- * @see IStatusBar#onDisplayReady(int)
+ * @see IStatusBar#onDisplayAddSystemDecorations(int)
*/
- default void onDisplayReady(int displayId) {
+ default void onDisplayAddSystemDecorations(int displayId) {
}
/**
@@ -1205,9 +1205,9 @@
}
@Override
- public void onDisplayReady(int displayId) {
+ public void onDisplayAddSystemDecorations(int displayId) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_DISPLAY_READY, displayId, 0).sendToTarget();
+ mHandler.obtainMessage(MSG_DISPLAY_ADD_SYSTEM_DECORATIONS, displayId, 0).sendToTarget();
}
}
@@ -1851,9 +1851,9 @@
mCallbacks.get(i).showPinningEscapeToast();
}
break;
- case MSG_DISPLAY_READY:
+ case MSG_DISPLAY_ADD_SYSTEM_DECORATIONS:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).onDisplayReady(msg.arg1);
+ mCallbacks.get(i).onDisplayAddSystemDecorations(msg.arg1);
}
break;
case MSG_DISPLAY_REMOVE_SYSTEM_DECORATIONS:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index d46638f..f5764d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -418,6 +418,7 @@
}
is OngoingActivityChipModel.Shown.Timer,
is OngoingActivityChipModel.Shown.Text,
+ is OngoingActivityChipModel.Shown.ShortTimeDelta,
is OngoingActivityChipModel.Shown.IconOnly -> {
chipView.accessibilityLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
index 13f4e51..375e029 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipContent.kt
@@ -95,6 +95,10 @@
is OngoingActivityChipModel.Shown.ShortTimeDelta -> {
// TODO(b/372657935): Implement ShortTimeDelta content in compose.
}
+
+ is OngoingActivityChipModel.Shown.IconOnly -> {
+ throw IllegalStateException("ChipContent should only be used if the chip shows text")
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
index c6d6da2..e0c7645 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt
@@ -40,7 +40,7 @@
}
/** This chip should be shown with the given information. */
- abstract class Shown(
+ sealed class Shown(
/** The icon to show on the chip. If null, no icon will be shown. */
open val icon: ChipIcon?,
/** What colors to use for the chip. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 2d1eccd..a0a8671 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -22,6 +22,7 @@
import com.android.internal.widget.MessagingMessage
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.Flags
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
@@ -144,7 +145,12 @@
)
log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
traceSection("updateNotifOnUiModeChanged") {
- mPipeline?.allNotifs?.forEach { entry -> entry.row?.onUiModeChanged() }
+ mPipeline?.allNotifs?.forEach { entry ->
+ entry.row?.onUiModeChanged()
+ if (Flags.notificationUndoGutsOnConfigChanged()) {
+ mGutsManager.closeAndUndoGuts()
+ }
+ }
}
}
@@ -152,9 +158,15 @@
colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
- val exposedGuts = entry.areGutsExposed()
- if (exposedGuts) {
- mGutsManager.onDensityOrFontScaleChanged(entry)
+ if (Flags.notificationUndoGutsOnConfigChanged()) {
+ mGutsManager.closeAndUndoGuts()
+ } else {
+ // This property actually gets reset when the guts are re-inflated, so we're never
+ // actually calling onDensityOrFontScaleChanged below.
+ val exposedGuts = entry.areGutsExposed()
+ if (exposedGuts) {
+ mGutsManager.onDensityOrFontScaleChanged(entry)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
index cb9bd4a..33c71d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/AODPromotedNotification.kt
@@ -27,8 +27,19 @@
import android.widget.ImageView
import android.widget.ProgressBar
import android.widget.TextView
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.key
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.view.isVisible
import com.android.app.tracing.traceSection
@@ -41,6 +52,8 @@
import com.android.internal.widget.NotificationRowIconView
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.res.R as systemuiR
+import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.PrimaryText
+import com.android.systemui.statusbar.notification.promoted.AodPromotedNotificationColor.SecondaryText
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style
import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When
@@ -59,31 +72,55 @@
key(content.identity) {
val layoutResource = content.layoutResource ?: return
- AndroidView(
- factory = { context ->
- traceSection("$TAG.inflate") {
- LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
- }
- .apply {
- setTag(
- viewUpdaterTagId,
- traceSection("$TAG.findViews") {
- AODPromotedNotificationViewUpdater(this)
- },
- )
- }
- },
- update = { view ->
- traceSection("$TAG.update") {
- (view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater).update(
- content
- )
- }
- },
- )
+ val topPadding = dimensionResource(systemuiR.dimen.below_clock_padding_start_icons)
+ val sidePaddings = dimensionResource(systemuiR.dimen.notification_side_paddings)
+ val paddingValues =
+ PaddingValues(top = topPadding, start = sidePaddings, end = sidePaddings, bottom = 0.dp)
+
+ val borderStroke = BorderStroke(1.dp, SecondaryText.brush)
+
+ val borderRadius = dimensionResource(systemuiR.dimen.notification_corner_radius)
+ val borderShape = RoundedCornerShape(borderRadius)
+
+ Box(modifier = Modifier.padding(paddingValues)) {
+ AODPromotedNotificationView(
+ layoutResource = layoutResource,
+ content = content,
+ modifier = Modifier.border(borderStroke, borderShape),
+ )
+ }
}
}
+@Composable
+fun AODPromotedNotificationView(
+ layoutResource: Int,
+ content: PromotedNotificationContentModel,
+ modifier: Modifier = Modifier,
+) {
+ AndroidView(
+ factory = { context ->
+ val view =
+ traceSection("$TAG.inflate") {
+ LayoutInflater.from(context).inflate(layoutResource, /* root= */ null)
+ }
+
+ val updater =
+ traceSection("$TAG.findViews") { AODPromotedNotificationViewUpdater(view) }
+
+ view.setTag(viewUpdaterTagId, updater)
+
+ view
+ },
+ update = { view ->
+ val updater = view.getTag(viewUpdaterTagId) as AODPromotedNotificationViewUpdater
+
+ traceSection("$TAG.update") { updater.update(content) }
+ },
+ modifier = modifier,
+ )
+}
+
private val PromotedNotificationContentModel.layoutResource: Int?
get() {
return if (Flags.notificationsRedesignTemplates()) {
@@ -262,12 +299,12 @@
}
private fun updateTitle(titleView: TextView?, content: PromotedNotificationContentModel) {
- updateTextView(titleView, content.title, color = Color.PrimaryText)
+ updateTextView(titleView, content.title, color = PrimaryText)
}
private fun updateTimeAndChronometer(content: PromotedNotificationContentModel) {
- setTextViewColor(time, Color.SecondaryText)
- setTextViewColor(chronometer, Color.SecondaryText)
+ setTextViewColor(time, SecondaryText)
+ setTextViewColor(chronometer, SecondaryText)
val timeValue = content.time
@@ -309,7 +346,7 @@
private fun updateTextView(
view: TextView?,
text: CharSequence?,
- color: Color = Color.SecondaryText,
+ color: AodPromotedNotificationColor = SecondaryText,
) {
setTextViewColor(view, color)
@@ -322,15 +359,19 @@
}
}
- private fun setTextViewColor(view: TextView?, color: Color) {
- view?.setTextColor(color.color.toInt())
+ private fun setTextViewColor(view: TextView?, color: AodPromotedNotificationColor) {
+ view?.setTextColor(color.colorInt)
}
+}
- private enum class Color(val color: UInt) {
- Background(0x00000000u),
- PrimaryText(0xFFFFFFFFu),
- SecondaryText(0xFFCCCCCCu),
- }
+private enum class AodPromotedNotificationColor(colorUInt: UInt) {
+ Background(0x00000000u),
+ PrimaryText(0xFFFFFFFFu),
+ SecondaryText(0xFFCCCCCCu);
+
+ val colorInt = colorUInt.toInt()
+ val color = Color(colorInt)
+ val brush = SolidColor(color)
}
private val viewUpdaterTagId = systemuiR.id.aod_promoted_notification_view_updater_tag
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
index b86d1d9..75d1c7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGuts.java
@@ -287,7 +287,7 @@
* @param save whether the state should be saved
* @param force whether the guts should be force-closed regardless of state.
*/
- private void closeControls(int x, int y, boolean save, boolean force) {
+ public void closeControls(int x, int y, boolean save, boolean force) {
// First try to dismiss any blocking helper.
if (getWindowToken() == null) {
if (mClosedListener != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index b1e5b22..445cd01 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -48,6 +48,7 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.settingslib.notification.ConversationIconFactory;
import com.android.systemui.CoreStartable;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -223,6 +224,10 @@
}
public void onDensityOrFontScaleChanged(NotificationEntry entry) {
+ if (!Flags.notificationUndoGutsOnConfigChanged()) {
+ Log.wtf(TAG, "onDensityOrFontScaleChanged should not be called if"
+ + " notificationUndoGutsOnConfigChanged is off");
+ }
setExposedGuts(entry.getGuts());
bindGuts(entry.getRow());
}
@@ -590,7 +595,8 @@
}
/**
- * Closes guts or notification menus that might be visible and saves any changes.
+ * Closes guts or notification menus that might be visible and saves any changes if applicable
+ * (see {@link NotificationGuts.GutsContent#shouldBeSavedOnClose}).
*
* @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed.
* @param force true if guts should be closed regardless of state (used for snooze only).
@@ -611,6 +617,20 @@
}
/**
+ * Closes all guts that might be visible without saving changes.
+ */
+ public void closeAndUndoGuts() {
+ if (mNotificationGutsExposed != null) {
+ mNotificationGutsExposed.removeCallbacks(mOpenRunnable);
+ mNotificationGutsExposed.closeControls(
+ /* x = */ -1,
+ /* y = */ -1,
+ /* save = */ false,
+ /* force = */ false);
+ }
+ }
+
+ /**
* Returns the exposed NotificationGuts or null if none are exposed.
*/
public NotificationGuts getExposedGuts() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
index 99a6f6a..83897f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSnooze.java
@@ -51,6 +51,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Flags;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.res.R;
@@ -86,18 +87,26 @@
private NotificationSwipeActionHelper mSnoozeListener;
private StatusBarNotification mSbn;
- private View mSnoozeView;
- private TextView mSelectedOptionText;
+ @VisibleForTesting
+ public View mSnoozeView;
+ @VisibleForTesting
+ public TextView mSelectedOptionText;
private TextView mUndoButton;
- private ImageView mExpandButton;
- private View mDivider;
- private ViewGroup mSnoozeOptionContainer;
- private List<SnoozeOption> mSnoozeOptions;
+ @VisibleForTesting
+ public ImageView mExpandButton;
+ @VisibleForTesting
+ public View mDivider;
+ @VisibleForTesting
+ public ViewGroup mSnoozeOptionContainer;
+ @VisibleForTesting
+ public List<SnoozeOption> mSnoozeOptions;
private int mCollapsedHeight;
private SnoozeOption mDefaultOption;
- private SnoozeOption mSelectedOption;
+ @VisibleForTesting
+ public SnoozeOption mSelectedOption;
private boolean mSnoozing;
- private boolean mExpanded;
+ @VisibleForTesting
+ public boolean mExpanded;
private AnimatorSet mExpandAnimation;
private KeyValueListParser mParser;
@@ -334,7 +343,8 @@
}
}
- private void showSnoozeOptions(boolean show) {
+ @VisibleForTesting
+ public void showSnoozeOptions(boolean show) {
int drawableId = show ? com.android.internal.R.drawable.ic_collapse_notification
: com.android.internal.R.drawable.ic_expand_notification;
mExpandButton.setImageResource(drawableId);
@@ -381,7 +391,8 @@
mExpandAnimation.start();
}
- private void setSelected(SnoozeOption option, boolean userAction) {
+ @VisibleForTesting
+ public void setSelected(SnoozeOption option, boolean userAction) {
if (option != mSelectedOption) {
mSelectedOption = option;
mSelectedOptionText.setText(option.getConfirmation());
@@ -466,7 +477,12 @@
@Override
public boolean handleCloseControls(boolean save, boolean force) {
- if (mExpanded && !force) {
+ if (Flags.notificationUndoGutsOnConfigChanged() && !save) {
+ // Undo changes and let the guts handle closing the view
+ mSelectedOption = null;
+ showSnoozeOptions(false);
+ return false;
+ } else if (mExpanded && !force) {
// Collapse expanded state on outside touch
showSnoozeOptions(false);
return true;
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
index 9795cda..eecea92 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/TutorialSelectionScreen.kt
@@ -27,6 +27,7 @@
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
+import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
@@ -82,18 +83,29 @@
}
),
) {
- val padding = if (hasCompactWindowSize()) 24.dp else 60.dp
+ val isCompactWindow = hasCompactWindowSize()
+ val padding = if (isCompactWindow) 24.dp else 60.dp
val configuration = LocalConfiguration.current
when (configuration.orientation) {
Configuration.ORIENTATION_LANDSCAPE -> {
- HorizontalSelectionButtons(
- onBackTutorialClicked = onBackTutorialClicked,
- onHomeTutorialClicked = onHomeTutorialClicked,
- onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
- onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(padding),
- lastSelectedScreen,
- )
+ if (isCompactWindow)
+ HorizontalCompactSelectionButtons(
+ onBackTutorialClicked = onBackTutorialClicked,
+ onHomeTutorialClicked = onHomeTutorialClicked,
+ onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
+ lastSelectedScreen,
+ modifier = Modifier.weight(1f).padding(padding),
+ )
+ else
+ HorizontalSelectionButtons(
+ onBackTutorialClicked = onBackTutorialClicked,
+ onHomeTutorialClicked = onHomeTutorialClicked,
+ onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
+ lastSelectedScreen,
+ modifier = Modifier.weight(1f).padding(padding),
+ )
}
else -> {
VerticalSelectionButtons(
@@ -101,8 +113,8 @@
onHomeTutorialClicked = onHomeTutorialClicked,
onRecentAppsTutorialClicked = onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked = onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).padding(padding),
lastSelectedScreen,
+ modifier = Modifier.weight(1f).padding(padding),
)
}
}
@@ -120,11 +132,99 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onSwitchAppsTutorialClicked: () -> Unit,
- modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
+) {
+ Column(modifier = modifier) {
+ TwoByTwoTutorialButtons(
+ onBackTutorialClicked,
+ onHomeTutorialClicked,
+ onRecentAppsTutorialClicked,
+ onSwitchAppsTutorialClicked,
+ lastSelectedScreen,
+ modifier = Modifier.weight(1f).fillMaxSize(),
+ )
+ }
+}
+
+@Composable
+private fun TwoByTwoTutorialButtons(
+ onBackTutorialClicked: () -> Unit,
+ onHomeTutorialClicked: () -> Unit,
+ onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
+ lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
+) {
+ val homeFocusRequester = remember { FocusRequester() }
+ val backFocusRequester = remember { FocusRequester() }
+ val recentAppsFocusRequester = remember { FocusRequester() }
+ val switchAppsFocusRequester = remember { FocusRequester() }
+ LaunchedEffect(Unit) {
+ when (lastSelectedScreen) {
+ Screen.HOME_GESTURE -> homeFocusRequester.requestFocus()
+ Screen.BACK_GESTURE -> backFocusRequester.requestFocus()
+ Screen.RECENT_APPS_GESTURE -> recentAppsFocusRequester.requestFocus()
+ Screen.SWITCH_APPS_GESTURE -> switchAppsFocusRequester.requestFocus()
+ else -> {} // No-Op.
+ }
+ }
+ Column {
+ Row(Modifier.weight(1f)) {
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_home_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_home_icon),
+ iconColor = MaterialTheme.colorScheme.onPrimary,
+ onClick = onHomeTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.primary,
+ modifier = modifier.focusRequester(homeFocusRequester).focusable().fillMaxSize(),
+ )
+ Spacer(modifier = Modifier.size(16.dp))
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_back_gesture_button),
+ icon = Icons.AutoMirrored.Outlined.ArrowBack,
+ iconColor = MaterialTheme.colorScheme.onTertiary,
+ onClick = onBackTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.tertiary,
+ modifier = modifier.focusRequester(backFocusRequester).focusable().fillMaxSize(),
+ )
+ }
+ Spacer(modifier = Modifier.size(16.dp))
+ Row(Modifier.weight(1f)) {
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_recent_apps_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_recents_icon),
+ iconColor = MaterialTheme.colorScheme.onSecondary,
+ onClick = onRecentAppsTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.secondary,
+ modifier =
+ modifier.focusRequester(recentAppsFocusRequester).focusable().fillMaxSize(),
+ )
+ Spacer(modifier = Modifier.size(16.dp))
+ TutorialButton(
+ text = stringResource(R.string.touchpad_tutorial_switch_apps_gesture_button),
+ icon = ImageVector.vectorResource(id = R.drawable.touchpad_tutorial_apps_icon),
+ iconColor = MaterialTheme.colorScheme.primary,
+ onClick = onSwitchAppsTutorialClicked,
+ backgroundColor = MaterialTheme.colorScheme.onPrimary,
+ modifier =
+ modifier.focusRequester(switchAppsFocusRequester).focusable().fillMaxSize(),
+ )
+ }
+ }
+}
+
+@Composable
+private fun HorizontalCompactSelectionButtons(
+ onBackTutorialClicked: () -> Unit,
+ onHomeTutorialClicked: () -> Unit,
+ onRecentAppsTutorialClicked: () -> Unit,
+ onSwitchAppsTutorialClicked: () -> Unit,
+ lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
) {
Row(
- horizontalArrangement = Arrangement.spacedBy(20.dp),
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = modifier,
) {
@@ -133,8 +233,8 @@
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).fillMaxSize(),
lastSelectedScreen,
+ modifier = Modifier.weight(1f).fillMaxSize(),
)
}
}
@@ -145,8 +245,8 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onSwitchAppsTutorialClicked: () -> Unit,
- modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
@@ -158,8 +258,8 @@
onHomeTutorialClicked,
onRecentAppsTutorialClicked,
onSwitchAppsTutorialClicked,
- modifier = Modifier.weight(1f).fillMaxSize(),
lastSelectedScreen,
+ modifier = Modifier.weight(1f).fillMaxSize(),
)
}
}
@@ -170,8 +270,8 @@
onHomeTutorialClicked: () -> Unit,
onRecentAppsTutorialClicked: () -> Unit,
onSwitchAppsTutorialClicked: () -> Unit,
- modifier: Modifier = Modifier,
lastSelectedScreen: Screen,
+ modifier: Modifier = Modifier,
) {
val homeFocusRequester = remember { FocusRequester() }
val backFocusRequester = remember { FocusRequester() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
index 2bd104d..48b801c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/authentication/domain/interactor/AuthenticationInteractorKosmos.kt
@@ -20,6 +20,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.user.domain.interactor.selectedUserInteractor
val Kosmos.authenticationInteractor by
@@ -29,5 +30,6 @@
backgroundDispatcher = testDispatcher,
repository = authenticationRepository,
selectedUserInteractor = selectedUserInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
new file mode 100644
index 0000000..edfe8ec
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/BatteryRepositoryKosmos.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2025 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.common.data.repository
+
+import com.android.systemui.kosmos.Kosmos
+
+val Kosmos.batteryRepository: BatteryRepository by Kosmos.Fixture { FakeBatteryRepository() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt
new file mode 100644
index 0000000..ac94335
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/data/repository/FakeBatteryRepository.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2025 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.common.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+class FakeBatteryRepository : BatteryRepository {
+ private val _isDevicePluggedIn = MutableStateFlow(false)
+
+ override val isDevicePluggedIn: Flow<Boolean> = _isDevicePluggedIn.asStateFlow()
+
+ fun setDevicePluggedIn(isPluggedIn: Boolean) {
+ _isDevicePluggedIn.value = isPluggedIn
+ }
+}
+
+val BatteryRepository.fake
+ get() = this as FakeBatteryRepository
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
new file mode 100644
index 0000000..2153955
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/common/domain/interactor/BatteryInteractorKosmos.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2025 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.common.domain.interactor
+
+import com.android.systemui.common.data.repository.batteryRepository
+import com.android.systemui.kosmos.Kosmos
+
+var Kosmos.batteryInteractor by Kosmos.Fixture { BatteryInteractor(batteryRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
index 1636257..603160d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalPrefsRepository.kt
@@ -25,7 +25,8 @@
/** Fake implementation of [CommunalPrefsRepository] */
class FakeCommunalPrefsRepository : CommunalPrefsRepository {
private val _isCtaDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
- private val _isHubOnboardingismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+ private val _isHubOnboardingDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
+ private val _isDreamButtonTooltipDismissed = MutableStateFlow<Set<UserInfo>>(emptySet())
override fun isCtaDismissed(user: UserInfo): Flow<Boolean> =
_isCtaDismissed.map { it.contains(user) }
@@ -35,10 +36,18 @@
}
override fun isHubOnboardingDismissed(user: UserInfo): Flow<Boolean> =
- _isHubOnboardingismissed.map { it.contains(user) }
+ _isHubOnboardingDismissed.map { it.contains(user) }
override suspend fun setHubOnboardingDismissed(user: UserInfo) {
- _isHubOnboardingismissed.value =
- _isHubOnboardingismissed.value.toMutableSet().apply { add(user) }
+ _isHubOnboardingDismissed.value =
+ _isHubOnboardingDismissed.value.toMutableSet().apply { add(user) }
+ }
+
+ override fun isDreamButtonTooltipDismissed(user: UserInfo): Flow<Boolean> =
+ _isDreamButtonTooltipDismissed.map { it.contains(user) }
+
+ override suspend fun setDreamButtonTooltipDismissed(user: UserInfo) {
+ _isDreamButtonTooltipDismissed.value =
+ _isDreamButtonTooltipDismissed.value.toMutableSet().apply { add(user) }
}
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index 89aad4b..b0a6de1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -19,10 +19,13 @@
import android.content.testableContext
import android.os.userManager
import com.android.systemui.broadcast.broadcastDispatcher
+import com.android.systemui.common.domain.interactor.batteryInteractor
import com.android.systemui.communal.data.repository.communalMediaRepository
import com.android.systemui.communal.data.repository.communalSmartspaceRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
+import com.android.systemui.communal.posturing.domain.interactor.posturingInteractor
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.dock.dockManager
import com.android.systemui.flags.Flags
import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -64,6 +67,9 @@
logBuffer = logcatLogBuffer("CommunalInteractor"),
tableLogBuffer = mock(),
managedProfileController = fakeManagedProfileController,
+ batteryInteractor = batteryInteractor,
+ dockManager = dockManager,
+ posturingInteractor = posturingInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index c2d2392..43d3eb7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -18,6 +18,7 @@
import android.service.dream.dreamManager
import com.android.internal.logging.uiEventLogger
+import com.android.systemui.communal.domain.interactor.communalPrefsInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
@@ -29,6 +30,7 @@
CommunalToDreamButtonViewModel(
backgroundContext = testDispatcher,
batteryController = batteryController,
+ prefsInteractor = communalPrefsInteractor,
settingsInteractor = communalSettingsInteractor,
activityStarter = activityStarter,
dreamManager = dreamManager,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
index 1d3fd30..c927b55 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractorKosmos.kt
@@ -22,6 +22,7 @@
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.scene.domain.interactor.sceneBackInteractor
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -36,5 +37,6 @@
alternateBouncerInteractor = alternateBouncerInteractor,
dismissCallbackRegistry = dismissCallbackRegistry,
sceneBackInteractor = sceneBackInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
index e4c7df6..9e36428 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/deviceentry/domain/interactor/DeviceUnlockedInteractorKosmos.kt
@@ -25,6 +25,7 @@
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.util.settings.data.repository.userAwareSecureSettingsRepository
@@ -40,6 +41,7 @@
systemPropertiesHelper = fakeSystemPropertiesHelper,
userAwareSecureSettingsRepository = userAwareSecureSettingsRepository,
keyguardInteractor = keyguardInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
.apply { activateIn(testScope) }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
index b07de16..ff7a06c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.domain.interactor
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.keyguard.data.repository.keyguardTransitionRepository
import com.android.systemui.kosmos.Kosmos
@@ -41,5 +43,7 @@
communalSettingsInteractor = communalSettingsInteractor,
swipeToDismissInteractor = swipeToDismissInteractor,
keyguardOcclusionInteractor = keyguardOcclusionInteractor,
+ communalInteractor = communalInteractor,
+ communalSceneInteractor = communalSceneInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
index e46ede6..e9ba425 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneBackInteractorKosmos.kt
@@ -18,6 +18,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.scene.sceneContainerConfig
import com.android.systemui.scene.shared.logger.sceneLogger
@@ -25,5 +26,6 @@
SceneBackInteractor(
logger = sceneLogger,
sceneContainerConfig = sceneContainerConfig,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index d105326..65bfafb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -36,6 +36,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testScope
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.model.sysUiState
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.scene.domain.interactor.disabledContentInteractor
@@ -89,5 +90,6 @@
disabledContentInteractor = disabledContentInteractor,
activityTransitionAnimator = activityTransitionAnimator,
shadeModeInteractor = shadeModeInteractor,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
index a4631f1..2ba9c80 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/domain/interactor/ShadeModeInteractorKosmos.kt
@@ -21,6 +21,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.table.logcatTableLogBuffer
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.fakeShadeRepository
import com.android.systemui.shade.data.repository.shadeRepository
@@ -31,6 +32,7 @@
applicationScope = applicationCoroutineScope,
repository = shadeRepository,
secureSettingsRepository = fakeSecureSettingsRepository,
+ tableLogBuffer = logcatTableLogBuffer(this, "sceneFrameworkTableLogBuffer"),
)
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index fda57d6..e422fef 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -31,6 +31,7 @@
import android.os.PowerManager;
import android.os.SystemClock;
import android.provider.Settings;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -71,9 +72,9 @@
*/
class AccessibilityInputFilter extends InputFilter implements EventStreamTransformation {
- private static final String TAG = AccessibilityInputFilter.class.getSimpleName();
+ private static final String TAG = "A11yInputFilter";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
/**
* Flag for enabling the screen magnification feature.
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 8e0a778..67fdca4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -36,6 +36,7 @@
import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCAST_RECEIVER;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.content.Context.DEVICE_ID_DEFAULT;
+import static android.hardware.input.InputSettings.isRepeatKeysFeatureFlagEnabled;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_GESTURE;
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR;
@@ -156,6 +157,7 @@
import android.view.MagnificationSpec;
import android.view.MotionEvent;
import android.view.SurfaceControl;
+import android.view.ViewConfiguration;
import android.view.WindowInfo;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
@@ -3494,6 +3496,7 @@
somethingChanged |= readMagnificationFollowTypingLocked(userState);
somethingChanged |= readAlwaysOnMagnificationLocked(userState);
somethingChanged |= readMouseKeysEnabledLocked(userState);
+ somethingChanged |= readRepeatKeysSettingsLocked(userState);
return somethingChanged;
}
@@ -5771,6 +5774,12 @@
private final Uri mUserSetupCompleteUri = Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE);
+ private final Uri mRepeatKeysEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.KEY_REPEAT_ENABLED);
+
+ private final Uri mRepeatKeysTimeoutMsUri = Settings.Secure.getUriFor(
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS);
+
public AccessibilityContentObserver(Handler handler) {
super(handler);
}
@@ -5827,6 +5836,12 @@
mNavigationModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mUserSetupCompleteUri, false, this, UserHandle.USER_ALL);
+ if (isRepeatKeysFeatureFlagEnabled() && Flags.enableMagnificationKeyboardControl()) {
+ contentResolver.registerContentObserver(
+ mRepeatKeysEnabledUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mRepeatKeysTimeoutMsUri, false, this, UserHandle.USER_ALL);
+ }
}
@Override
@@ -5917,6 +5932,9 @@
}
} else if (mNavigationModeUri.equals(uri) || mUserSetupCompleteUri.equals(uri)) {
updateShortcutsForCurrentNavigationMode();
+ } else if (mRepeatKeysEnabledUri.equals(uri)
+ || mRepeatKeysTimeoutMsUri.equals(uri)) {
+ readRepeatKeysSettingsLocked(userState);
}
}
}
@@ -6055,6 +6073,24 @@
return false;
}
+ boolean readRepeatKeysSettingsLocked(AccessibilityUserState userState) {
+ if (!isRepeatKeysFeatureFlagEnabled() || !Flags.enableMagnificationKeyboardControl()) {
+ return false;
+ }
+ final boolean isRepeatKeysEnabled = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_ENABLED,
+ 1, userState.mUserId) == 1;
+ final int repeatKeysTimeoutMs = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
+ ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT, userState.mUserId);
+ mMagnificationController.setRepeatKeysEnabled(isRepeatKeysEnabled);
+ mMagnificationController.setRepeatKeysTimeoutMs(repeatKeysTimeoutMs);
+
+ // No need to update any other state, so always return false.
+ return false;
+ }
+
boolean readMouseKeysEnabledLocked(AccessibilityUserState userState) {
if (!keyboardA11yMouseKeys()) {
return false;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index a3fe9ec..6cba363 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -554,7 +554,8 @@
if (motionEventInjector != null
&& mWindowManagerService.isTouchOrFaketouchDevice()) {
motionEventInjector.injectEvents(
- gestureSteps.getList(), mClient, sequence, displayId);
+ gestureSteps.getList(), mClient, sequence, displayId,
+ mAccessibilityServiceInfo.isAccessibilityTool());
} else {
try {
if (svcClientTracingEnabled()) {
diff --git a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
index 5cbd1a2..b216953 100644
--- a/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
+++ b/services/accessibility/java/com/android/server/accessibility/MotionEventInjector.java
@@ -105,12 +105,14 @@
* either complete or cancelled.
*/
public void injectEvents(List<GestureStep> gestureSteps,
- IAccessibilityServiceClient serviceInterface, int sequence, int displayId) {
+ IAccessibilityServiceClient serviceInterface, int sequence, int displayId,
+ boolean fromAccessibilityTool) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = gestureSteps;
args.arg2 = serviceInterface;
args.argi1 = sequence;
args.argi2 = displayId;
+ args.argi3 = fromAccessibilityTool ? 1 : 0;
mHandler.sendMessage(mHandler.obtainMessage(MESSAGE_INJECT_EVENTS, args));
}
@@ -132,9 +134,11 @@
return;
}
cancelAnyPendingInjectedEvents();
- // Indicate that the input event is injected from accessibility, to let applications
- // distinguish it from events injected by other means.
- policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+ if (!android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+ // Indicate that the input event is injected from accessibility, to let applications
+ // distinguish it from events injected by other means.
+ policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+ }
sendMotionEventToNext(event, rawEvent, policyFlags);
}
@@ -159,8 +163,12 @@
public boolean handleMessage(Message message) {
if (message.what == MESSAGE_INJECT_EVENTS) {
SomeArgs args = (SomeArgs) message.obj;
- injectEventsMainThread((List<GestureStep>) args.arg1,
- (IAccessibilityServiceClient) args.arg2, args.argi1, args.argi2);
+ injectEventsMainThread(
+ /*gestureSteps=*/(List<GestureStep>) args.arg1,
+ /*serviceInterface=*/(IAccessibilityServiceClient) args.arg2,
+ /*sequence=*/args.argi1,
+ /*displayId=*/args.argi2,
+ /*fromAccessibilityTool=*/args.argi3 == 1);
args.recycle();
return true;
}
@@ -169,9 +177,15 @@
return false;
}
MotionEvent motionEvent = (MotionEvent) message.obj;
- sendMotionEventToNext(motionEvent, motionEvent,
- WindowManagerPolicyConstants.FLAG_PASS_TO_USER
- | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY);
+ int policyFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+ if (android.view.accessibility.Flags.preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+ boolean fromAccessibilityTool = message.arg2 == 1;
+ if (fromAccessibilityTool) {
+ policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
+ }
+ }
+ sendMotionEventToNext(motionEvent, motionEvent, policyFlags);
boolean isEndOfSequence = message.arg1 != 0;
if (isEndOfSequence) {
notifyService(mServiceInterfaceForCurrentGesture, mSequencesInProgress.get(0), true);
@@ -181,7 +195,8 @@
}
private void injectEventsMainThread(List<GestureStep> gestureSteps,
- IAccessibilityServiceClient serviceInterface, int sequence, int displayId) {
+ IAccessibilityServiceClient serviceInterface, int sequence, int displayId,
+ boolean fromAccessibilityTool) {
if (mIsDestroyed) {
try {
serviceInterface.onPerformGestureResult(sequence, false);
@@ -228,7 +243,8 @@
event.setDisplayId(displayId);
int isEndOfSequence = (i == events.size() - 1) ? 1 : 0;
Message message = mHandler.obtainMessage(
- MESSAGE_SEND_MOTION_EVENT, isEndOfSequence, 0, event);
+ MESSAGE_SEND_MOTION_EVENT, isEndOfSequence,
+ fromAccessibilityTool ? 1 : 0, event);
mLastScheduledEventTime = event.getEventTime();
mHandler.sendMessageDelayed(message, Math.max(0, event.getEventTime() - currentTime));
}
@@ -322,9 +338,16 @@
long now = SystemClock.uptimeMillis();
MotionEvent cancelEvent =
obtainMotionEvent(now, now, MotionEvent.ACTION_CANCEL, getLastTouchPoints(), 1);
- sendMotionEventToNext(cancelEvent, cancelEvent,
- WindowManagerPolicyConstants.FLAG_PASS_TO_USER
- | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY);
+ int policyFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
+ if (android.view.accessibility.Flags
+ .preventA11yNontoolFromInjectingIntoSensitiveViews()) {
+ // ACTION_CANCEL events are internal system details for event stream state
+ // management and not used for performing new actions, so always treat them as
+ // originating from an accessibility tool.
+ policyFlags |= WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL;
+ }
+ sendMotionEventToNext(cancelEvent, cancelEvent, policyFlags);
mOpenGesturesInProgress.put(source, false);
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
index 486f1f4..e757dd5 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationController.java
@@ -50,6 +50,7 @@
import android.util.SparseLongArray;
import android.util.TypedValue;
import android.view.Display;
+import android.view.ViewConfiguration;
import android.view.accessibility.MagnificationAnimationCallback;
import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
@@ -122,9 +123,8 @@
private @ZoomDirection int mActiveZoomDirection = ZOOM_DIRECTION_IN;
private int mActiveZoomDisplay = Display.INVALID_DISPLAY;
- // TODO(b/355499907): Get initial repeat interval from repeat keys settings.
- @VisibleForTesting
- public static final int INITIAL_KEYBOARD_REPEAT_INTERVAL_MS = 500;
+ private int mInitialKeyboardRepeatIntervalMs =
+ ViewConfiguration.DEFAULT_LONG_PRESS_TIMEOUT;
@VisibleForTesting
public static final int KEYBOARD_REPEAT_INTERVAL_MS = 60;
@@ -321,12 +321,6 @@
mAlwaysOnMagnificationFeatureFlag = new AlwaysOnMagnificationFeatureFlag(context);
mAlwaysOnMagnificationFeatureFlag.addOnChangedListener(
mBackgroundExecutor, mAms::updateAlwaysOnMagnification);
-
- // TODO(b/355499907): Add an observer for repeat keys enabled changes,
- // rather than initializing once at startup.
- mRepeatKeysEnabled = Settings.Secure.getIntForUser(
- mContext.getContentResolver(), Settings.Secure.KEY_REPEAT_ENABLED, 1,
- UserHandle.USER_CURRENT) != 0;
}
@VisibleForTesting
@@ -383,7 +377,7 @@
if (mRepeatKeysEnabled) {
mHandler.sendMessageDelayed(
PooledLambda.obtainMessage(MagnificationController::maybeContinuePan, this),
- INITIAL_KEYBOARD_REPEAT_INTERVAL_MS);
+ mInitialKeyboardRepeatIntervalMs);
}
}
@@ -404,7 +398,7 @@
if (mRepeatKeysEnabled) {
mHandler.sendMessageDelayed(
PooledLambda.obtainMessage(MagnificationController::maybeContinueZoom, this),
- INITIAL_KEYBOARD_REPEAT_INTERVAL_MS);
+ mInitialKeyboardRepeatIntervalMs);
}
}
@@ -434,6 +428,19 @@
}
}
+ public void setRepeatKeysEnabled(boolean isRepeatKeysEnabled) {
+ mRepeatKeysEnabled = isRepeatKeysEnabled;
+ }
+
+ public void setRepeatKeysTimeoutMs(int repeatKeysTimeoutMs) {
+ mInitialKeyboardRepeatIntervalMs = repeatKeysTimeoutMs;
+ }
+
+ @VisibleForTesting
+ public int getInitialKeyboardRepeatIntervalMs() {
+ return mInitialKeyboardRepeatIntervalMs;
+ }
+
private void handleUserInteractionChanged(int displayId, int mode) {
if (mMagnificationCapabilities != Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_ALL) {
return;
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index aef1c08..d47aab0 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -4673,12 +4673,6 @@
keep.add(providerId);
// Use the new AppWidgetProviderInfo.
provider.setPartialInfoLocked(info);
- // Clear old previews
- if (remoteViewsProto()) {
- clearGeneratedPreviewsAsync(provider);
- } else {
- provider.clearGeneratedPreviewsLocked();
- }
// If it's enabled
final int M = provider.widgets.size();
if (M > 0) {
@@ -5104,6 +5098,10 @@
AndroidFuture<RemoteViews> result = new AndroidFuture<>();
mSavePreviewsHandler.post(() -> {
SparseArray<RemoteViews> previews = loadGeneratedPreviews(provider);
+ if (previews.size() == 0 && provider.info.generatedPreviewCategories != 0) {
+ // Failed to read previews from file, clear the file and update providers.
+ saveGeneratedPreviews(provider, previews, /* notify= */ true);
+ }
for (int i = 0; i < previews.size(); i++) {
if ((widgetCategory & previews.keyAt(i)) != 0) {
result.complete(previews.valueAt(i));
@@ -5222,8 +5220,14 @@
continue;
}
ProtoInputStream input = new ProtoInputStream(previewsFile.readFully());
- provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
- input);
+ try {
+ provider.info.generatedPreviewCategories = readGeneratedPreviewCategoriesFromProto(
+ input);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to read generated previews from file for " + provider, e);
+ previewsFile.delete();
+ provider.info.generatedPreviewCategories = 0;
+ }
if (DEBUG) {
Slog.i(TAG, TextUtils.formatSimple(
"loadGeneratedPreviewCategoriesLocked %d %s categories %d", profileId,
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 3de84f1..d2db8f7 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -27,7 +27,6 @@
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
-import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
import android.os.UEventObserver;
@@ -37,6 +36,7 @@
import android.util.Slog;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -57,8 +57,6 @@
final class DockObserver extends SystemService {
private static final String TAG = "DockObserver";
- private static final int MSG_DOCK_STATE_CHANGED = 0;
-
private final PowerManager mPowerManager;
private final PowerManager.WakeLock mWakeLock;
@@ -66,11 +64,16 @@
private boolean mSystemReady;
+ @GuardedBy("mLock")
private int mActualDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ @GuardedBy("mLock")
private int mReportedDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+
+ @GuardedBy("mLock")
private int mPreviousDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ @GuardedBy("mLock")
private boolean mUpdatesStopped;
private final boolean mKeepDreamingWhenUnplugging;
@@ -182,18 +185,24 @@
ExtconInfo.EXTCON_DOCK
});
- if (!infos.isEmpty()) {
- ExtconInfo info = infos.get(0);
- Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath()
- + ", statePath: " + info.getStatePath());
+ synchronized (mLock) {
+ if (!infos.isEmpty()) {
+ ExtconInfo info = infos.get(0);
+ Slog.i(
+ TAG,
+ "Found extcon info devPath: "
+ + info.getDevicePath()
+ + ", statePath: "
+ + info.getStatePath());
- // set initial status
- setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
- mPreviousDockState = mActualDockState;
+ // set initial status
+ setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
+ mPreviousDockState = mActualDockState;
- mExtconUEventObserver.startObserving(info);
- } else {
- Slog.i(TAG, "No extcon dock device found in this kernel.");
+ mExtconUEventObserver.startObserving(info);
+ } else {
+ Slog.i(TAG, "No extcon dock device found in this kernel.");
+ }
}
mDockObserverLocalService = new DockObserverLocalService();
@@ -223,13 +232,15 @@
}
}
+ @GuardedBy("mLock")
private void updateIfDockedLocked() {
// don't bother broadcasting undocked here
if (mReportedDockState != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
- updateLocked();
+ postWakefulDockStateChange();
}
}
+ @GuardedBy("mLock")
private void setActualDockStateLocked(int newState) {
mActualDockState = newState;
if (!mUpdatesStopped) {
@@ -237,6 +248,7 @@
}
}
+ @GuardedBy("mLock")
private void setDockStateLocked(int newState) {
if (newState != mReportedDockState) {
mReportedDockState = newState;
@@ -246,10 +258,12 @@
if (mSystemReady) {
// Wake up immediately when docked or undocked unless prohibited from doing so.
if (allowWakeFromDock()) {
- mPowerManager.wakeUp(SystemClock.uptimeMillis(),
+ mPowerManager.wakeUp(
+ SystemClock.uptimeMillis(),
+ PowerManager.WAKE_REASON_DOCK,
"android.server:DOCK");
}
- updateLocked();
+ postWakefulDockStateChange();
}
}
}
@@ -263,9 +277,8 @@
Settings.Global.THEATER_MODE_ON, 0) == 0);
}
- private void updateLocked() {
- mWakeLock.acquire();
- mHandler.sendEmptyMessage(MSG_DOCK_STATE_CHANGED);
+ private void postWakefulDockStateChange() {
+ mHandler.post(mWakeLock.wrap(this::handleDockStateChange));
}
private void handleDockStateChange() {
@@ -348,17 +361,7 @@
}
}
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_DOCK_STATE_CHANGED:
- handleDockStateChange();
- mWakeLock.release();
- break;
- }
- }
- };
+ private final Handler mHandler = new Handler(true /*async*/);
private int getDockedStateExtraValue(ExtconStateProvider state) {
for (ExtconStateConfig config : mExtconStateConfigs) {
@@ -386,6 +389,7 @@
}
}
+ @GuardedBy("mLock")
private void setDockStateFromProviderLocked(ExtconStateProvider provider) {
int state = Intent.EXTRA_DOCK_STATE_UNDOCKED;
if ("1".equals(provider.getValue("DOCK"))) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 0603c45..6ece265 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -19397,13 +19397,13 @@
if (((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED) == 0)
&& intent.getExtras() != null && intent.getExtras().hasIntent()) {
Slog.wtf(TAG,
- "[IntentRedirect] The intent does not have its nested keys collected as a "
+ "[IntentRedirect Hardening] The intent does not have its nested keys collected as a "
+ "preparation for creating intent creator tokens. Intent: "
+ intent + "; creatorPackage: " + creatorPackage);
if (preventIntentRedirectShowToastIfNestedKeysNotCollectedRW()) {
UiThread.getHandler().post(
() -> Toast.makeText(mContext,
- "Nested keys not collected. go/report-bug-intentRedir to report a"
+ "Nested keys not collected, activity launch won't be blocked. go/report-bug-intentRedir to report a"
+ " bug", Toast.LENGTH_LONG).show());
}
if (preventIntentRedirectThrowExceptionIfNestedKeysNotCollected()) {
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index d335529..ce526e5 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -572,6 +572,7 @@
public long mTotalAnonMemFreedKBs;
public long mSumOrigAnonRss;
public double mMaxCompactEfficiency;
+ public double mMaxSwapEfficiency;
// Cpu time
public long mTotalCpuTimeMillis;
@@ -586,6 +587,10 @@
if (compactEfficiency > mMaxCompactEfficiency) {
mMaxCompactEfficiency = compactEfficiency;
}
+ final double swapEfficiency = anonRssSaved / (double) origAnonRss;
+ if (swapEfficiency > mMaxSwapEfficiency) {
+ mMaxSwapEfficiency = swapEfficiency;
+ }
mTotalDeltaAnonRssKBs += anonRssSaved;
mTotalZramConsumedKBs += zramConsumed;
mTotalAnonMemFreedKBs += memFreed;
@@ -628,7 +633,11 @@
pw.println(" -----Memory Stats----");
pw.println(" Total Delta Anon RSS (KB) : " + mTotalDeltaAnonRssKBs);
pw.println(" Total Physical ZRAM Consumed (KB): " + mTotalZramConsumedKBs);
+ // Anon Mem Freed = Delta Anon RSS - ZRAM Consumed
pw.println(" Total Anon Memory Freed (KB): " + mTotalAnonMemFreedKBs);
+ pw.println(" Avg Swap Efficiency (KB) (Delta Anon RSS/Orig Anon RSS): "
+ + (mTotalDeltaAnonRssKBs / (double) mSumOrigAnonRss));
+ pw.println(" Max Swap Efficiency: " + mMaxSwapEfficiency);
// This tells us how much anon memory we were able to free thanks to running
// compaction
pw.println(" Avg Compaction Efficiency (Anon Freed/Anon RSS): "
@@ -808,8 +817,9 @@
pw.println(" Tracking last compaction stats for " + mLastCompactionStats.size()
+ " processes.");
pw.println("Last Compaction per process stats:");
- pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
- + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs"
+ + ",SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,"
+ + "oomAdjReason)");
for (Map.Entry<Integer, SingleCompactionStats> entry :
mLastCompactionStats.entrySet()) {
SingleCompactionStats stats = entry.getValue();
@@ -818,7 +828,8 @@
pw.println();
pw.println("Last 20 Compactions Stats:");
pw.println(" (ProcessName,Source,DeltaAnonRssKBs,ZramConsumedKBs,AnonMemFreedKBs,"
- + "CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,oomAdjReason)");
+ + "SwapEfficiency,CompactEfficiency,CompactCost(ms/MB),procState,oomAdj,"
+ + "oomAdjReason)");
for (SingleCompactionStats stats : mCompactionStatsHistory) {
stats.dump(pw);
}
@@ -1779,6 +1790,8 @@
double getCompactEfficiency() { return mAnonMemFreedKBs / (double) mOrigAnonRss; }
+ double getSwapEfficiency() { return mDeltaAnonRssKBs / (double) mOrigAnonRss; }
+
double getCompactCost() {
// mCpuTimeMillis / (anonMemFreedKBs/1024) and metric is in (ms/MB)
return mCpuTimeMillis / (double) mAnonMemFreedKBs * 1024;
@@ -1791,7 +1804,8 @@
@NeverCompile
void dump(PrintWriter pw) {
pw.println(" (" + mProcessName + "," + mSourceType.name() + "," + mDeltaAnonRssKBs
- + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + "," + getCompactEfficiency()
+ + "," + mZramConsumedKBs + "," + mAnonMemFreedKBs + ","
+ + getSwapEfficiency() + "," + getCompactEfficiency()
+ "," + getCompactCost() + "," + mProcState + "," + mOomAdj + ","
+ OomAdjuster.oomAdjReasonToString(mOomAdjReason) + ")");
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5f118cb..f283009 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -837,6 +837,7 @@
private final Executor mAudioServerLifecycleExecutor;
private long mSysPropListenerNativeHandle;
+ private CacheWatcher mCacheWatcher;
private final List<Future> mScheduledPermissionTasks = new ArrayList();
private IMediaProjectionManager mProjectionService; // to validate projection token
@@ -11093,31 +11094,26 @@
}, getAudioPermissionsDelay(), TimeUnit.MILLISECONDS));
}
};
- mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
- PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
- task);
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ mCacheWatcher = new CacheWatcher(task);
+ mCacheWatcher.start();
+ } else {
+ mSysPropListenerNativeHandle = mAudioSystem.listenForSystemPropertyChange(
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
+ task);
+ }
} else {
mAudioSystem.listenForSystemPropertyChange(
PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY,
() -> mAudioServerLifecycleExecutor.execute(
mPermissionProvider::onPermissionStateChanged));
}
-
- if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
- new PackageInfoTransducer().start();
- }
}
/**
- * A transducer that converts high-speed changes in the CACHE_KEY_PACKAGE_INFO_CACHE
- * PropertyInvalidatedCache into low-speed changes in the CACHE_KEY_PACKAGE_INFO_NOTIFY system
- * property. This operates on the popcorn principle: changes in the source are done when the
- * source has been quiet for the soak interval.
- *
- * TODO(b/381097912) This is a temporary measure to support migration away from sysprop
- * sniffing. It should be cleaned up.
+ * Listens for CACHE_KEY_PACKAGE_INFO_CACHE invalidations to trigger permission syncing
*/
- private static class PackageInfoTransducer extends Thread {
+ private static class CacheWatcher extends Thread {
// The run/stop signal.
private final AtomicBoolean mRunning = new AtomicBoolean(false);
@@ -11125,81 +11121,33 @@
// The source of change information.
private final PropertyInvalidatedCache.NonceWatcher mWatcher;
- // The handler for scheduling delayed reactions to changes.
- private final Handler mHandler;
+ // Task to trigger when cache changes
+ private final Runnable mTask;
- // How long to soak changes: 50ms is the legacy choice.
- private final static long SOAK_TIME_MS = 50;
-
- // The ubiquitous lock.
- private final Object mLock = new Object();
-
- // If positive, this is the soak expiration time.
- @GuardedBy("mLock")
- private long mSoakDeadlineMs = -1;
-
- // A source of unique long values.
- @GuardedBy("mLock")
- private long mToken = 0;
-
- PackageInfoTransducer() {
- mWatcher = PropertyInvalidatedCache
- .getNonceWatcher(PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
- mHandler = new Handler(BackgroundThread.getHandler().getLooper()) {
- @Override
- public void handleMessage(Message msg) {
- PackageInfoTransducer.this.handleMessage(msg);
- }};
+ public CacheWatcher(Runnable r) {
+ mWatcher = PropertyInvalidatedCache.getNonceWatcher(
+ PermissionManager.CACHE_KEY_PACKAGE_INFO_CACHE);
+ mTask = r;
}
public void run() {
mRunning.set(true);
while (mRunning.get()) {
+ doCheck();
try {
- final int changes = mWatcher.waitForChange();
- if (changes == 0 || !mRunning.get()) {
- continue;
- }
+ mWatcher.waitForChange();
} catch (InterruptedException e) {
+ Log.wtf(TAG, "Unexpected Interrupt", e);
// We don't know why the exception occurred but keep running until told to
// stop.
continue;
}
- trigger();
}
}
- @GuardedBy("mLock")
- private void updateLocked() {
- String n = Long.toString(mToken++);
- SystemPropertySetter.setWithRetry(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
- }
-
- private void trigger() {
- synchronized (mLock) {
- boolean alreadyQueued = mSoakDeadlineMs >= 0;
- final long nowMs = SystemClock.uptimeMillis();
- mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
- if (!alreadyQueued) {
- mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
- updateLocked();
- }
- }
- }
-
- private void handleMessage(Message msg) {
- synchronized (mLock) {
- if (mSoakDeadlineMs < 0) {
- return; // ???
- }
- final long nowMs = SystemClock.uptimeMillis();
- if (mSoakDeadlineMs > nowMs) {
- mSoakDeadlineMs = nowMs + SOAK_TIME_MS;
- mHandler.sendEmptyMessageAtTime(0, mSoakDeadlineMs);
- return;
- }
- mSoakDeadlineMs = -1;
- updateLocked();
+ public synchronized void doCheck() {
+ if (mWatcher.isChanged()) {
+ mTask.run();
}
}
@@ -15376,7 +15324,11 @@
/** @see AudioManager#permissionUpdateBarrier() */
public void permissionUpdateBarrier() {
if (!audioserverPermissions()) return;
- mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+ if (PropertyInvalidatedCache.separatePermissionNotificationsEnabled()) {
+ mCacheWatcher.doCheck();
+ } else {
+ mAudioSystem.triggerSystemPropertyUpdate(mSysPropListenerNativeHandle);
+ }
List<Future> snapshot;
synchronized (mScheduledPermissionTasks) {
snapshot = List.copyOf(mScheduledPermissionTasks);
diff --git a/services/core/java/com/android/server/display/feature/display_flags.aconfig b/services/core/java/com/android/server/display/feature/display_flags.aconfig
index bbd0e41..7890db1 100644
--- a/services/core/java/com/android/server/display/feature/display_flags.aconfig
+++ b/services/core/java/com/android/server/display/feature/display_flags.aconfig
@@ -509,3 +509,11 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "display_category_built_in"
+ namespace: "display_manager"
+ description: "Add a new category to get the built in displays."
+ bug: "293651324"
+ is_fixed_read_only: false
+}
diff --git a/services/core/java/com/android/server/input/KeyboardLayoutManager.java b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
index b8ce86b..2f22853 100644
--- a/services/core/java/com/android/server/input/KeyboardLayoutManager.java
+++ b/services/core/java/com/android/server/input/KeyboardLayoutManager.java
@@ -22,8 +22,6 @@
import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_VIRTUAL_KEYBOARD;
import static android.hardware.input.KeyboardLayoutSelectionResult.LAYOUT_SELECTION_CRITERIA_DEFAULT;
-import static com.android.hardware.input.Flags.keyboardLayoutManagerMultiUserImeSetup;
-
import android.annotation.AnyThread;
import android.annotation.MainThread;
import android.annotation.NonNull;
@@ -68,7 +66,6 @@
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.internal.R;
@@ -1081,8 +1078,6 @@
List<ImeInfo> imeInfoList = new ArrayList<>();
UserManager userManager = Objects.requireNonNull(
mContext.getSystemService(UserManager.class));
- InputMethodManager inputMethodManager = Objects.requireNonNull(
- mContext.getSystemService(InputMethodManager.class));
// Need to use InputMethodManagerInternal to call getEnabledInputMethodListAsUser()
// instead of using InputMethodManager which uses enforceCallingPermissions() that
// breaks when we are calling the method for work profile user ID since it doesn't check
@@ -1093,14 +1088,10 @@
for (InputMethodInfo imeInfo :
inputMethodManagerInternal.getEnabledInputMethodListAsUser(
userId)) {
- final List<InputMethodSubtype> imeSubtypes;
- if (keyboardLayoutManagerMultiUserImeSetup()) {
- imeSubtypes = inputMethodManagerInternal.getEnabledInputMethodSubtypeListAsUser(
- imeInfo.getId(), true /* allowsImplicitlyEnabledSubtypes */, userId);
- } else {
- imeSubtypes = inputMethodManager.getEnabledInputMethodSubtypeList(imeInfo,
- true /* allowsImplicitlyEnabledSubtypes */);
- }
+ final List<InputMethodSubtype> imeSubtypes =
+ inputMethodManagerInternal.getEnabledInputMethodSubtypeListAsUser(
+ imeInfo.getId(), true /* allowsImplicitlyEnabledSubtypes */,
+ userId);
for (InputMethodSubtype imeSubtype : imeSubtypes) {
if (!imeSubtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
continue;
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index d23a863..d00ac4d 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -29,14 +29,13 @@
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.hardware.tv.mediaquality.AmbientBacklightColorFormat;
-import android.hardware.tv.mediaquality.DolbyAudioProcessing;
-import android.hardware.tv.mediaquality.DtsVirtualX;
import android.hardware.tv.mediaquality.IMediaQuality;
import android.hardware.tv.mediaquality.IPictureProfileAdjustmentListener;
import android.hardware.tv.mediaquality.IPictureProfileChangedListener;
import android.hardware.tv.mediaquality.ISoundProfileAdjustmentListener;
import android.hardware.tv.mediaquality.ISoundProfileChangedListener;
import android.hardware.tv.mediaquality.ParamCapability;
+import android.hardware.tv.mediaquality.ParameterRange;
import android.hardware.tv.mediaquality.PictureParameter;
import android.hardware.tv.mediaquality.PictureParameters;
import android.hardware.tv.mediaquality.SoundParameter;
@@ -50,8 +49,6 @@
import android.media.quality.IPictureProfileCallback;
import android.media.quality.ISoundProfileCallback;
import android.media.quality.MediaQualityContract.BaseParameters;
-import android.media.quality.MediaQualityContract.PictureQuality;
-import android.media.quality.MediaQualityContract.SoundQuality;
import android.media.quality.MediaQualityManager;
import android.media.quality.ParameterCapability;
import android.media.quality.PictureProfile;
@@ -77,21 +74,16 @@
import com.android.server.SystemService;
import com.android.server.utils.Slogf;
-import org.json.JSONException;
-import org.json.JSONObject;
-
import java.io.File;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
-import java.util.UUID;
import java.util.stream.Collectors;
/**
@@ -106,7 +98,6 @@
private static final String PICTURE_PROFILE_PREFERENCE = "picture_profile_preference";
private static final String SOUND_PROFILE_PREFERENCE = "sound_profile_preference";
private static final String COMMA_DELIMITER = ",";
- private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
private final Context mContext;
private final MediaQualityDbHelper mMediaQualityDbHelper;
private final BiMap<Long, String> mPictureProfileTempIdMap;
@@ -275,7 +266,7 @@
synchronized (mPictureProfileLock) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- ContentValues values = getContentValues(null,
+ ContentValues values = MediaQualityUtils.getContentValues(null,
pp.getProfileType(),
pp.getName(),
pp.getPackageName() == null || pp.getPackageName().isEmpty()
@@ -286,7 +277,7 @@
// id is auto-generated by SQLite upon successful insertion of row
Long id = db.insert(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
null, values);
- populateTempIdMap(mPictureProfileTempIdMap, id);
+ MediaQualityUtils.populateTempIdMap(mPictureProfileTempIdMap, id);
String value = mPictureProfileTempIdMap.getValue(id);
pp.setProfileId(value);
notifyOnPictureProfileAdded(value, pp, Binder.getCallingUid(),
@@ -308,8 +299,9 @@
private android.hardware.tv.mediaquality.PictureProfile convertToHalPictureProfile(Long id,
PersistableBundle params) {
PictureParameters pictureParameters = new PictureParameters();
- pictureParameters.pictureParameters = convertPersistableBundleToPictureParameterList(
- params);
+ pictureParameters.pictureParameters =
+ MediaQualityUtils.convertPersistableBundleToPictureParameterList(
+ params);
android.hardware.tv.mediaquality.PictureProfile toReturn =
new android.hardware.tv.mediaquality.PictureProfile();
@@ -329,7 +321,7 @@
}
synchronized (mPictureProfileLock) {
- ContentValues values = getContentValues(dbId,
+ ContentValues values = MediaQualityUtils.getContentValues(dbId,
pp.getProfileType(),
pp.getName(),
pp.getPackageName(),
@@ -405,7 +397,7 @@
try (
Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- getMediaProfileColumns(includeParams), selection,
+ MediaQualityUtils.getMediaProfileColumns(includeParams), selection,
selectionArguments)
) {
int count = cursor.getCount();
@@ -420,7 +412,8 @@
return null;
}
cursor.moveToFirst();
- return convertCursorToPictureProfileWithTempId(cursor);
+ return MediaQualityUtils.convertCursorToPictureProfileWithTempId(cursor,
+ mPictureProfileTempIdMap);
}
}
}
@@ -432,7 +425,8 @@
try (
Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
- getMediaProfileColumns(false), selection, selectionArguments)
+ MediaQualityUtils.getMediaProfileColumns(false), selection,
+ selectionArguments)
) {
int count = cursor.getCount();
if (count == 0) {
@@ -445,7 +439,8 @@
return null;
}
cursor.moveToFirst();
- return convertCursorToPictureProfileWithTempId(cursor);
+ return MediaQualityUtils.convertCursorToPictureProfileWithTempId(cursor,
+ mPictureProfileTempIdMap);
}
}
@@ -463,7 +458,8 @@
options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
- return getPictureProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
+ return getPictureProfilesBasedOnConditions(MediaQualityUtils
+ .getMediaProfileColumns(includeParams),
selection, selectionArguments);
}
}
@@ -492,8 +488,8 @@
try {
if (mMediaQuality != null) {
- PictureParameter[] pictureParameters =
- convertPersistableBundleToPictureParameterList(params);
+ PictureParameter[] pictureParameters = MediaQualityUtils
+ .convertPersistableBundleToPictureParameterList(params);
PictureParameters pp = new PictureParameters();
pp.pictureParameters = pictureParameters;
@@ -507,337 +503,6 @@
return false;
}
- private PictureParameter[] convertPersistableBundleToPictureParameterList(
- PersistableBundle params) {
- if (params == null) {
- return null;
- }
-
- List<PictureParameter> pictureParams = new ArrayList<>();
- if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
- pictureParams.add(PictureParameter.brightness(params.getLong(
- PictureQuality.PARAMETER_BRIGHTNESS)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_CONTRAST)) {
- pictureParams.add(PictureParameter.contrast(params.getInt(
- PictureQuality.PARAMETER_CONTRAST)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_SHARPNESS)) {
- pictureParams.add(PictureParameter.sharpness(params.getInt(
- PictureQuality.PARAMETER_SHARPNESS)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_SATURATION)) {
- pictureParams.add(PictureParameter.saturation(params.getInt(
- PictureQuality.PARAMETER_SATURATION)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_HUE)) {
- pictureParams.add(PictureParameter.hue(params.getInt(
- PictureQuality.PARAMETER_HUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
- pictureParams.add(PictureParameter.colorTunerBrightness(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
- pictureParams.add(PictureParameter.colorTunerSaturation(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
- pictureParams.add(PictureParameter.colorTunerHue(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
- pictureParams.add(PictureParameter.colorTunerRedOffset(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
- pictureParams.add(PictureParameter.colorTunerGreenOffset(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
- pictureParams.add(PictureParameter.colorTunerBlueOffset(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
- pictureParams.add(PictureParameter.colorTunerRedGain(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
- pictureParams.add(PictureParameter.colorTunerGreenGain(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
- pictureParams.add(PictureParameter.colorTunerBlueGain(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
- pictureParams.add(PictureParameter.noiseReduction(
- (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
- pictureParams.add(PictureParameter.mpegNoiseReduction(
- (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
- pictureParams.add(PictureParameter.fleshTone(
- (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
- pictureParams.add(PictureParameter.deContour(
- (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
- pictureParams.add(PictureParameter.dynamicLumaControl(
- (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
- pictureParams.add(PictureParameter.filmMode(params.getBoolean(
- PictureQuality.PARAMETER_FILM_MODE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_BLUE_STRETCH)) {
- pictureParams.add(PictureParameter.blueStretch(params.getBoolean(
- PictureQuality.PARAMETER_BLUE_STRETCH)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNE)) {
- pictureParams.add(PictureParameter.colorTune(params.getBoolean(
- PictureQuality.PARAMETER_COLOR_TUNE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
- pictureParams.add(PictureParameter.colorTemperature(
- (byte) params.getInt(
- PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
- pictureParams.add(PictureParameter.globeDimming(params.getBoolean(
- PictureQuality.PARAMETER_GLOBAL_DIMMING)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
- pictureParams.add(PictureParameter.autoPictureQualityEnabled(params.getBoolean(
- PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
- pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
- PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
- pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
- pictureParams.add(PictureParameter.colorTemperatureGreenGain(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
- pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
- pictureParams.add(PictureParameter.levelRange(
- (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
- pictureParams.add(PictureParameter.gamutMapping(params.getBoolean(
- PictureQuality.PARAMETER_GAMUT_MAPPING)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_PC_MODE)) {
- pictureParams.add(PictureParameter.pcMode(params.getBoolean(
- PictureQuality.PARAMETER_PC_MODE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_LOW_LATENCY)) {
- pictureParams.add(PictureParameter.lowLatency(params.getBoolean(
- PictureQuality.PARAMETER_LOW_LATENCY)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_VRR)) {
- pictureParams.add(PictureParameter.vrr(params.getBoolean(
- PictureQuality.PARAMETER_VRR)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_CVRR)) {
- pictureParams.add(PictureParameter.cvrr(params.getBoolean(
- PictureQuality.PARAMETER_CVRR)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
- pictureParams.add(PictureParameter.hdmiRgbRange(
- (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
- pictureParams.add(PictureParameter.colorSpace(
- (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
- pictureParams.add(PictureParameter.panelInitMaxLuminceNits(
- params.getInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
- pictureParams.add(PictureParameter.panelInitMaxLuminceValid(
- params.getBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
- pictureParams.add(PictureParameter.gamma(
- (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
- pictureParams.add(PictureParameter.colorTemperatureRedOffset(params.getInt(
- PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
- pictureParams.add(PictureParameter.colorTemperatureGreenOffset(params.getInt(
- PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
- pictureParams.add(PictureParameter.colorTemperatureBlueOffset(params.getInt(
- PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
- pictureParams.add(PictureParameter.elevenPointRed(params.getIntArray(
- PictureQuality.PARAMETER_ELEVEN_POINT_RED)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
- pictureParams.add(PictureParameter.elevenPointGreen(params.getIntArray(
- PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
- pictureParams.add(PictureParameter.elevenPointBlue(params.getIntArray(
- PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
- pictureParams.add(PictureParameter.lowBlueLight(
- (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
- pictureParams.add(PictureParameter.LdMode(
- (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
- pictureParams.add(PictureParameter.osdRedGain(params.getInt(
- PictureQuality.PARAMETER_OSD_RED_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
- pictureParams.add(PictureParameter.osdGreenGain(params.getInt(
- PictureQuality.PARAMETER_OSD_GREEN_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
- pictureParams.add(PictureParameter.osdBlueGain(params.getInt(
- PictureQuality.PARAMETER_OSD_BLUE_GAIN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
- pictureParams.add(PictureParameter.osdRedOffset(params.getInt(
- PictureQuality.PARAMETER_OSD_RED_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
- pictureParams.add(PictureParameter.osdGreenOffset(params.getInt(
- PictureQuality.PARAMETER_OSD_GREEN_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
- pictureParams.add(PictureParameter.osdBlueOffset(params.getInt(
- PictureQuality.PARAMETER_OSD_BLUE_OFFSET)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_HUE)) {
- pictureParams.add(PictureParameter.osdHue(params.getInt(
- PictureQuality.PARAMETER_OSD_HUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_SATURATION)) {
- pictureParams.add(PictureParameter.osdSaturation(params.getInt(
- PictureQuality.PARAMETER_OSD_SATURATION)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_OSD_CONTRAST)) {
- pictureParams.add(PictureParameter.osdContrast(params.getInt(
- PictureQuality.PARAMETER_OSD_CONTRAST)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
- pictureParams.add(PictureParameter.colorTunerSwitch(params.getBoolean(
- PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
- pictureParams.add(PictureParameter.colorTunerHueRed(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
- pictureParams.add(PictureParameter.colorTunerHueGreen(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
- pictureParams.add(PictureParameter.colorTunerHueBlue(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
- pictureParams.add(PictureParameter.colorTunerHueCyan(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
- pictureParams.add(PictureParameter.colorTunerHueMagenta(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
- pictureParams.add(PictureParameter.colorTunerHueYellow(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
- pictureParams.add(PictureParameter.colorTunerHueFlesh(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
- pictureParams.add(PictureParameter.colorTunerSaturationRed(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
- pictureParams.add(PictureParameter.colorTunerSaturationGreen(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
- pictureParams.add(PictureParameter.colorTunerSaturationBlue(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
- pictureParams.add(PictureParameter.colorTunerSaturationCyan(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
- pictureParams.add(PictureParameter.colorTunerSaturationMagenta(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
- pictureParams.add(PictureParameter.colorTunerSaturationYellow(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
- pictureParams.add(PictureParameter.colorTunerSaturationFlesh(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceRed(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceGreen(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceBlue(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceCyan(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceMagenta(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceYellow(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
- pictureParams.add(PictureParameter.colorTunerLuminanceFlesh(params.getInt(
- PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)));
- }
- if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
- pictureParams.add(PictureParameter.pictureQualityEventType(
- (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
- }
- return (PictureParameter[]) pictureParams.toArray();
- }
-
@GuardedBy("mPictureProfileLock")
@Override
public List<String> getPictureProfilePackageNames(UserHandle user) {
@@ -903,7 +568,7 @@
synchronized (mSoundProfileLock) {
SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- ContentValues values = getContentValues(null,
+ ContentValues values = MediaQualityUtils.getContentValues(null,
sp.getProfileType(),
sp.getName(),
sp.getPackageName() == null || sp.getPackageName().isEmpty()
@@ -914,7 +579,7 @@
// id is auto-generated by SQLite upon successful insertion of row
Long id = db.insert(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
null, values);
- populateTempIdMap(mSoundProfileTempIdMap, id);
+ MediaQualityUtils.populateTempIdMap(mSoundProfileTempIdMap, id);
String value = mSoundProfileTempIdMap.getValue(id);
sp.setProfileId(value);
notifyOnSoundProfileAdded(value, sp, Binder.getCallingUid(),
@@ -935,7 +600,8 @@
private android.hardware.tv.mediaquality.SoundProfile convertToHalSoundProfile(Long id,
PersistableBundle params) {
SoundParameters soundParameters = new SoundParameters();
- soundParameters.soundParameters = convertPersistableBundleToSoundParameterList(params);
+ soundParameters.soundParameters =
+ MediaQualityUtils.convertPersistableBundleToSoundParameterList(params);
android.hardware.tv.mediaquality.SoundProfile toReturn =
new android.hardware.tv.mediaquality.SoundProfile();
@@ -955,18 +621,19 @@
}
synchronized (mSoundProfileLock) {
- ContentValues values = getContentValues(dbId,
+ ContentValues values = MediaQualityUtils.getContentValues(dbId,
sp.getProfileType(),
sp.getName(),
sp.getPackageName(),
sp.getInputId(),
sp.getParameters());
- SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
- db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
- notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
- getSoundProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
- notifyHalOnSoundProfileChange(dbId, sp.getParameters());
+ SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+ db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+ null, values);
+ notifyOnSoundProfileUpdated(mSoundProfileTempIdMap.getValue(dbId),
+ getSoundProfile(dbId), Binder.getCallingUid(), Binder.getCallingPid());
+ notifyHalOnSoundProfileChange(dbId, sp.getParameters());
}
}
@@ -1029,7 +696,7 @@
try (
Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
- getMediaProfileColumns(includeParams), selection,
+ MediaQualityUtils.getMediaProfileColumns(includeParams), selection,
selectionArguments)
) {
int count = cursor.getCount();
@@ -1044,7 +711,8 @@
return null;
}
cursor.moveToFirst();
- return convertCursorToSoundProfileWithTempId(cursor);
+ return MediaQualityUtils.convertCursorToSoundProfileWithTempId(cursor,
+ mSoundProfileTempIdMap);
}
}
}
@@ -1056,7 +724,8 @@
try (
Cursor cursor = getCursorAfterQuerying(
mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
- getMediaProfileColumns(false), selection, selectionArguments)
+ MediaQualityUtils.getMediaProfileColumns(false), selection,
+ selectionArguments)
) {
int count = cursor.getCount();
if (count == 0) {
@@ -1069,7 +738,8 @@
return null;
}
cursor.moveToFirst();
- return convertCursorToSoundProfileWithTempId(cursor);
+ return MediaQualityUtils.convertCursorToSoundProfileWithTempId(
+ cursor, mSoundProfileTempIdMap);
}
}
@@ -1087,7 +757,8 @@
options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
String[] selectionArguments = {packageName};
- return getSoundProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
+ return getSoundProfilesBasedOnConditions(MediaQualityUtils
+ .getMediaProfileColumns(includeParams),
selection, selectionArguments);
}
}
@@ -1116,7 +787,7 @@
try {
if (mMediaQuality != null) {
SoundParameter[] soundParameters =
- convertPersistableBundleToSoundParameterList(params);
+ MediaQualityUtils.convertPersistableBundleToSoundParameterList(params);
SoundParameters sp = new SoundParameters();
sp.soundParameters = soundParameters;
@@ -1130,95 +801,6 @@
return false;
}
- private SoundParameter[] convertPersistableBundleToSoundParameterList(
- PersistableBundle params) {
- //TODO: set EqualizerDetail
- if (params == null) {
- return null;
- }
- List<SoundParameter> soundParams = new ArrayList<>();
- if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
- soundParams.add(SoundParameter.balance(params.getInt(
- SoundQuality.PARAMETER_BALANCE)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_BASS)) {
- soundParams.add(SoundParameter.bass(params.getInt(SoundQuality.PARAMETER_BASS)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_TREBLE)) {
- soundParams.add(SoundParameter.treble(params.getInt(
- SoundQuality.PARAMETER_TREBLE)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_SURROUND_SOUND)) {
- soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
- SoundQuality.PARAMETER_SURROUND_SOUND)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS)) {
- soundParams.add(SoundParameter.speakersEnabled(params.getBoolean(
- SoundQuality.PARAMETER_SPEAKERS)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
- soundParams.add(SoundParameter.speakersDelayMs(params.getInt(
- SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
- soundParams.add(SoundParameter.autoVolumeControl(params.getBoolean(
- SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_DTS_DRC)) {
- soundParams.add(SoundParameter.dtsDrc(params.getBoolean(
- SoundQuality.PARAMETER_DTS_DRC)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
- soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
- SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_EARC)) {
- soundParams.add(SoundParameter.enhancedAudioReturnChannelEnabled(params.getBoolean(
- SoundQuality.PARAMETER_EARC)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_DOWN_MIX_MODE)) {
- soundParams.add(SoundParameter.downmixMode((byte) params.getInt(
- SoundQuality.PARAMETER_DOWN_MIX_MODE)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_SOUND_STYLE)) {
- soundParams.add(SoundParameter.soundStyle((byte) params.getInt(
- SoundQuality.PARAMETER_SOUND_STYLE)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)) {
- soundParams.add(SoundParameter.digitalOutput((byte) params.getInt(
- SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)));
- }
- if (params.containsKey(SoundQuality.PARAMETER_DIALOGUE_ENHANCER)) {
- soundParams.add(SoundParameter.dolbyDialogueEnhancer((byte) params.getInt(
- SoundQuality.PARAMETER_DIALOGUE_ENHANCER)));
- }
-
- DolbyAudioProcessing dab = new DolbyAudioProcessing();
- dab.soundMode =
- (byte) params.getInt(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE);
- dab.volumeLeveler =
- params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER);
- dab.surroundVirtualizer = params.getBoolean(
- SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER);
- dab.dolbyAtmos =
- params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS);
- soundParams.add(SoundParameter.dolbyAudioProcessing(dab));
-
- DtsVirtualX dts = new DtsVirtualX();
- dts.tbHdx = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TBHDX);
- dts.limiter = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_LIMITER);
- dts.truSurroundX = params.getBoolean(
- SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X);
- dts.truVolumeHd = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD);
- dts.dialogClarity = params.getBoolean(
- SoundQuality.PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY);
- dts.definition = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DEFINITION);
- dts.height = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_HEIGHT);
- soundParams.add(SoundParameter.dtsVirtualX(dts));
-
- return (SoundParameter[]) soundParams.toArray();
- }
-
@GuardedBy("mSoundProfileLock")
@Override
public List<String> getSoundProfilePackageNames(UserHandle user) {
@@ -1269,169 +851,6 @@
mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
}
- private void populateTempIdMap(BiMap<Long, String> map, Long id) {
- if (id != null && map.getValue(id) == null) {
- String uuid;
- int attempts = 0;
- while (attempts < MAX_UUID_GENERATION_ATTEMPTS) {
- uuid = UUID.randomUUID().toString();
- if (map.getKey(uuid) == null) {
- map.put(id, uuid);
- return;
- }
- attempts++;
- }
- }
- }
-
- private String persistableBundleToJson(PersistableBundle bundle) {
- JSONObject json = new JSONObject();
- for (String key : bundle.keySet()) {
- Object value = bundle.get(key);
- try {
- if (value instanceof String) {
- json.put(key, bundle.getString(key));
- } else if (value instanceof Integer) {
- json.put(key, bundle.getInt(key));
- } else if (value instanceof Long) {
- json.put(key, bundle.getLong(key));
- } else if (value instanceof Boolean) {
- json.put(key, bundle.getBoolean(key));
- } else if (value instanceof Double) {
- json.put(key, bundle.getDouble(key));
- }
- } catch (JSONException e) {
- Log.e(TAG, "Unable to serialize ", e);
- }
- }
- return json.toString();
- }
-
- private PersistableBundle jsonToPersistableBundle(String jsonString) {
- PersistableBundle bundle = new PersistableBundle();
- if (jsonString != null) {
- JSONObject jsonObject = null;
- try {
- jsonObject = new JSONObject(jsonString);
-
- Iterator<String> keys = jsonObject.keys();
- while (keys.hasNext()) {
- String key = keys.next();
- Object value = jsonObject.get(key);
-
- if (value instanceof String) {
- bundle.putString(key, (String) value);
- } else if (value instanceof Integer) {
- bundle.putInt(key, (Integer) value);
- } else if (value instanceof Boolean) {
- bundle.putBoolean(key, (Boolean) value);
- } else if (value instanceof Double) {
- bundle.putDouble(key, (Double) value);
- } else if (value instanceof Long) {
- bundle.putLong(key, (Long) value);
- }
- }
- } catch (JSONException e) {
- throw new RuntimeException(e);
- }
- }
- return bundle;
- }
-
- private ContentValues getContentValues(Long dbId, Integer profileType, String name,
- String packageName, String inputId, PersistableBundle params) {
- ContentValues values = new ContentValues();
- if (dbId != null) {
- values.put(BaseParameters.PARAMETER_ID, dbId);
- }
- if (profileType != null) {
- values.put(BaseParameters.PARAMETER_TYPE, profileType);
- }
- if (name != null) {
- values.put(BaseParameters.PARAMETER_NAME, name);
- }
- if (packageName != null) {
- values.put(BaseParameters.PARAMETER_PACKAGE, packageName);
- }
- if (inputId != null) {
- values.put(BaseParameters.PARAMETER_INPUT_ID, inputId);
- }
- if (params != null) {
- values.put(mMediaQualityDbHelper.SETTINGS, persistableBundleToJson(params));
- }
- return values;
- }
-
- private String[] getMediaProfileColumns(boolean includeParams) {
- ArrayList<String> columns = new ArrayList<>(Arrays.asList(
- BaseParameters.PARAMETER_ID,
- BaseParameters.PARAMETER_TYPE,
- BaseParameters.PARAMETER_NAME,
- BaseParameters.PARAMETER_INPUT_ID,
- BaseParameters.PARAMETER_PACKAGE)
- );
- if (includeParams) {
- columns.add(mMediaQualityDbHelper.SETTINGS);
- }
- return columns.toArray(new String[0]);
- }
-
- private PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor) {
- return new PictureProfile(
- getTempId(mPictureProfileTempIdMap, cursor),
- getType(cursor),
- getName(cursor),
- getInputId(cursor),
- getPackageName(cursor),
- jsonToPersistableBundle(getSettingsString(cursor)),
- PictureProfileHandle.NONE
- );
- }
-
- private SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor) {
- return new SoundProfile(
- getTempId(mSoundProfileTempIdMap, cursor),
- getType(cursor),
- getName(cursor),
- getInputId(cursor),
- getPackageName(cursor),
- jsonToPersistableBundle(getSettingsString(cursor)),
- SoundProfileHandle.NONE
- );
- }
-
- private String getTempId(BiMap<Long, String> map, Cursor cursor) {
- int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
- Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
- populateTempIdMap(map, dbId);
- return map.getValue(dbId);
- }
-
- private int getType(Cursor cursor) {
- int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE);
- return colIndex != -1 ? cursor.getInt(colIndex) : 0;
- }
-
- private String getName(Cursor cursor) {
- int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME);
- return colIndex != -1 ? cursor.getString(colIndex) : null;
- }
-
- private String getInputId(Cursor cursor) {
- int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID);
- return colIndex != -1 ? cursor.getString(colIndex) : null;
- }
-
- private String getPackageName(Cursor cursor) {
- int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE);
- return colIndex != -1 ? cursor.getString(colIndex) : null;
- }
-
- private String getSettingsString(Cursor cursor) {
- int colIndex = cursor.getColumnIndex(mMediaQualityDbHelper.SETTINGS);
- return colIndex != -1 ? cursor.getString(colIndex) : null;
- }
-
private Cursor getCursorAfterQuerying(String table, String[] columns, String selection,
String[] selectionArgs) {
SQLiteDatabase db = mMediaQualityDbHelper.getReadableDatabase();
@@ -1448,7 +867,8 @@
) {
List<PictureProfile> pictureProfiles = new ArrayList<>();
while (cursor.moveToNext()) {
- pictureProfiles.add(convertCursorToPictureProfileWithTempId(cursor));
+ pictureProfiles.add(MediaQualityUtils.convertCursorToPictureProfileWithTempId(
+ cursor, mPictureProfileTempIdMap));
}
return pictureProfiles;
}
@@ -1463,7 +883,8 @@
) {
List<SoundProfile> soundProfiles = new ArrayList<>();
while (cursor.moveToNext()) {
- soundProfiles.add(convertCursorToSoundProfileWithTempId(cursor));
+ soundProfiles.add(MediaQualityUtils.convertCursorToSoundProfileWithTempId(
+ cursor, mSoundProfileTempIdMap));
}
return soundProfiles;
}
@@ -1713,7 +1134,39 @@
@Override
public List<ParameterCapability> getParameterCapabilities(
List<String> names, UserHandle user) {
- return new ArrayList<>();
+ byte[] byteArray = MediaQualityUtils.convertParameterToByteArray(names);
+ ParamCapability[] caps = new ParamCapability[byteArray.length];
+ try {
+ mMediaQuality.getParamCaps(byteArray, caps);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get parameter capabilities", e);
+ }
+
+ return getListParameterCapability(caps);
+ }
+
+ private List<ParameterCapability> getListParameterCapability(ParamCapability[] caps) {
+ List<ParameterCapability> pcList = new ArrayList<>();
+ for (ParamCapability pcHal : caps) {
+ String name = MediaQualityUtils.getParameterName(pcHal.name);
+ boolean isSupported = pcHal.isSupported;
+ int type = pcHal.defaultValue == null ? 0 : pcHal.defaultValue.getTag() + 1;
+ Bundle bundle = convertToCaps(pcHal.range);
+
+ pcList.add(new ParameterCapability(name, isSupported, type, bundle));
+ }
+ return pcList;
+ }
+
+ private Bundle convertToCaps(ParameterRange range) {
+ Bundle bundle = new Bundle();
+ bundle.putObject("INT_MIN_MAX", range.numRange.getIntMinMax());
+ bundle.putObject("INT_VALUES_SUPPORTED", range.numRange.getIntValuesSupported());
+ bundle.putObject("DOUBLE_MIN_MAX", range.numRange.getDoubleMinMax());
+ bundle.putObject("DOUBLE_VALUES_SUPPORTED", range.numRange.getDoubleValuesSupported());
+ bundle.putObject("LONG_MIN_MAX", range.numRange.getLongMinMax());
+ bundle.putObject("LONG_VALUES_SUPPORTED", range.numRange.getLongValuesSupported());
+ return bundle;
}
@GuardedBy("mPictureProfileLock")
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
new file mode 100644
index 0000000..5bd4420
--- /dev/null
+++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java
@@ -0,0 +1,1543 @@
+/*
+ * Copyright (C) 2025 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.server.media.quality;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.hardware.tv.mediaquality.DolbyAudioProcessing;
+import android.hardware.tv.mediaquality.DtsVirtualX;
+import android.hardware.tv.mediaquality.ParameterName;
+import android.hardware.tv.mediaquality.PictureParameter;
+import android.hardware.tv.mediaquality.SoundParameter;
+import android.media.quality.MediaQualityContract.BaseParameters;
+import android.media.quality.MediaQualityContract.PictureQuality;
+import android.media.quality.MediaQualityContract.SoundQuality;
+import android.media.quality.PictureProfile;
+import android.media.quality.PictureProfileHandle;
+import android.media.quality.SoundProfile;
+import android.media.quality.SoundProfileHandle;
+import android.os.PersistableBundle;
+import android.util.Log;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * Utility class for media quality framework.
+ *
+ * @hide
+ */
+public final class MediaQualityUtils {
+
+ private static final int MAX_UUID_GENERATION_ATTEMPTS = 10;
+ private static final String TAG = "MediaQualityUtils";
+ public static final String SETTINGS = "settings";
+
+ /**
+ * Convert PictureParameter List to PersistableBundle.
+ */
+ public static PersistableBundle convertPictureParameterListToPersistableBundle(
+ PictureParameter[] parameters) {
+ PersistableBundle bundle = new PersistableBundle();
+ for (PictureParameter pp : parameters) {
+ if (pp.getBrightness() > -1) {
+ bundle.putLong(PictureQuality.PARAMETER_BRIGHTNESS, (long) pp.getBrightness());
+ }
+ if (pp.getContrast() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_CONTRAST, pp.getContrast());
+ }
+ if (pp.getSharpness() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_SHARPNESS, pp.getSharpness());
+ }
+ if (pp.getSaturation() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_SATURATION, pp.getSaturation());
+ }
+ if (pp.getHue() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_HUE, pp.getHue());
+ }
+ if (pp.getColorTunerBrightness() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS,
+ pp.getColorTunerBrightness());
+ }
+ if (pp.getColorTunerSaturation() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION,
+ pp.getColorTunerSaturation());
+ }
+ if (pp.getColorTunerHue() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE, pp.getColorTunerHue());
+ }
+ if (pp.getColorTunerRedOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET,
+ pp.getColorTunerRedOffset());
+ }
+ if (pp.getColorTunerGreenOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET,
+ pp.getColorTunerGreenOffset());
+ }
+ if (pp.getColorTunerBlueOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET,
+ pp.getColorTunerBlueOffset());
+ }
+ if (pp.getColorTunerRedGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN,
+ pp.getColorTunerRedGain());
+ }
+ if (pp.getColorTunerGreenGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN,
+ pp.getColorTunerGreenGain());
+ }
+ if (pp.getColorTunerBlueGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN,
+ pp.getColorTunerBlueGain());
+ }
+ if (pp.getNoiseReduction() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_NOISE_REDUCTION,
+ pp.getNoiseReduction());
+ }
+ if (pp.getMpegNoiseReduction() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION,
+ pp.getMpegNoiseReduction());
+ }
+ if (pp.getFleshTone() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_FLESH_TONE, pp.getFleshTone());
+ }
+ if (pp.getDeContour() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_DECONTOUR, pp.getDeContour());
+ }
+ if (pp.getDynamicLumaControl() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL,
+ pp.getDynamicLumaControl());
+ }
+ if (pp.getColorTemperature() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE,
+ pp.getColorTemperature());
+ }
+ if (pp.getColorTemperatureRedGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN,
+ pp.getColorTemperatureRedGain());
+ }
+ if (pp.getColorTemperatureGreenGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN,
+ pp.getColorTemperatureGreenGain());
+ }
+ if (pp.getColorTemperatureBlueGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN,
+ pp.getColorTemperatureBlueGain());
+ }
+ if (pp.getLevelRange() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_LEVEL_RANGE, pp.getLevelRange());
+ }
+ if (pp.getHdmiRgbRange() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE, pp.getHdmiRgbRange());
+ }
+ if (pp.getColorSpace() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_SPACE, pp.getColorSpace());
+ }
+ if (pp.getPanelInitMaxLuminceNits() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS,
+ pp.getPanelInitMaxLuminceNits());
+ }
+ if (pp.getGamma() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_GAMMA, pp.getGamma());
+ }
+ if (pp.getColorTemperatureRedOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET,
+ pp.getColorTemperatureRedOffset());
+ }
+ if (pp.getColorTemperatureGreenOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET,
+ pp.getColorTemperatureGreenOffset());
+ }
+ if (pp.getColorTemperatureBlueOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET,
+ pp.getColorTemperatureBlueOffset());
+ }
+ if (pp.getLowBlueLight() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT, pp.getLowBlueLight());
+ }
+ if (pp.getLdMode() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_LD_MODE, pp.getLdMode());
+ }
+ if (pp.getOsdRedGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_RED_GAIN, pp.getOsdRedGain());
+ }
+ if (pp.getOsdGreenGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_GREEN_GAIN, pp.getOsdGreenGain());
+ }
+ if (pp.getOsdBlueGain() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_BLUE_GAIN, pp.getOsdBlueGain());
+ }
+ if (pp.getOsdRedOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_RED_OFFSET, pp.getOsdRedOffset());
+ }
+ if (pp.getOsdGreenOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_GREEN_OFFSET,
+ pp.getOsdGreenOffset());
+ }
+ if (pp.getOsdBlueOffset() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_BLUE_OFFSET, pp.getOsdBlueOffset());
+ }
+ if (pp.getOsdHue() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_HUE, pp.getOsdHue());
+ }
+ if (pp.getOsdSaturation() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_SATURATION, pp.getOsdSaturation());
+ }
+ if (pp.getOsdContrast() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_OSD_CONTRAST, pp.getOsdContrast());
+ }
+ if (pp.getColorTunerHueRed() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED,
+ pp.getColorTunerHueRed());
+ }
+ if (pp.getColorTunerHueGreen() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN,
+ pp.getColorTunerHueGreen());
+ }
+ if (pp.getColorTunerHueBlue() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE,
+ pp.getColorTunerHueBlue());
+ }
+ if (pp.getColorTunerHueCyan() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN,
+ pp.getColorTunerHueCyan());
+ }
+ if (pp.getColorTunerHueMagenta() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA,
+ pp.getColorTunerHueMagenta());
+ }
+ if (pp.getColorTunerHueYellow() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW,
+ pp.getColorTunerHueYellow());
+ }
+ if (pp.getColorTunerHueFlesh() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH,
+ pp.getColorTunerHueFlesh());
+ }
+ if (pp.getColorTunerSaturationRed() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED,
+ pp.getColorTunerSaturationRed());
+ }
+ if (pp.getColorTunerSaturationGreen() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN,
+ pp.getColorTunerSaturationGreen());
+ }
+ if (pp.getColorTunerSaturationBlue() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE,
+ pp.getColorTunerSaturationBlue());
+ }
+ if (pp.getColorTunerSaturationCyan() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN,
+ pp.getColorTunerSaturationCyan());
+ }
+ if (pp.getColorTunerSaturationMagenta() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA,
+ pp.getColorTunerSaturationMagenta());
+ }
+ if (pp.getColorTunerSaturationYellow() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW,
+ pp.getColorTunerSaturationYellow());
+ }
+ if (pp.getColorTunerSaturationFlesh() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH,
+ pp.getColorTunerSaturationFlesh());
+ }
+ if (pp.getColorTunerLuminanceRed() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED,
+ pp.getColorTunerLuminanceRed());
+ }
+ if (pp.getColorTunerLuminanceGreen() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN,
+ pp.getColorTunerLuminanceGreen());
+ }
+ if (pp.getColorTunerLuminanceBlue() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE,
+ pp.getColorTunerLuminanceBlue());
+ }
+ if (pp.getColorTunerLuminanceCyan() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN,
+ pp.getColorTunerLuminanceCyan());
+ }
+ if (pp.getColorTunerLuminanceMagenta() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA,
+ pp.getColorTunerLuminanceMagenta());
+ }
+ if (pp.getColorTunerLuminanceYellow() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW,
+ pp.getColorTunerLuminanceYellow());
+ }
+ if (pp.getColorTunerLuminanceFlesh() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH,
+ pp.getColorTunerLuminanceFlesh());
+ }
+ if (pp.getPictureQualityEventType() > -1) {
+ bundle.putInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE,
+ pp.getPictureQualityEventType());
+ }
+ if (pp.getFilmMode()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_FILM_MODE, true);
+ }
+ if (pp.getBlueStretch()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_BLUE_STRETCH, true);
+ }
+ if (pp.getColorTune()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_COLOR_TUNE, true);
+ }
+ if (pp.getGlobeDimming()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_GLOBAL_DIMMING, true);
+ }
+ if (pp.getAutoPictureQualityEnabled()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED, true);
+ }
+ if (pp.getAutoSuperResolutionEnabled()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED, true);
+ }
+ if (pp.getGamutMapping()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_GAMUT_MAPPING, true);
+ }
+ if (pp.getPcMode()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_PC_MODE, true);
+ }
+ if (pp.getLowLatency()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_LOW_LATENCY, true);
+ }
+ if (pp.getVrr()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_VRR, true);
+ }
+ if (pp.getCvrr()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_CVRR, true);
+ }
+ if (pp.getPanelInitMaxLuminceValid()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID, true);
+ }
+ if (pp.getColorTunerSwitch()) {
+ bundle.putBoolean(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH, true);
+ }
+ if (pp.getElevenPointRed() != null) {
+ bundle.putIntArray(PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+ pp.getElevenPointRed());
+ }
+ if (pp.getElevenPointBlue() != null) {
+ bundle.putIntArray(PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+ pp.getElevenPointBlue());
+ }
+ if (pp.getElevenPointGreen() != null) {
+ bundle.putIntArray(PictureQuality.PARAMETER_ELEVEN_POINT_RED,
+ pp.getElevenPointGreen());
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * Convert PersistableBundle to PictureParameter List.
+ */
+ public static PictureParameter[] convertPersistableBundleToPictureParameterList(
+ PersistableBundle params) {
+ List<PictureParameter> pictureParams = new ArrayList<>();
+ if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) {
+ pictureParams.add(PictureParameter.brightness(params.getLong(
+ PictureQuality.PARAMETER_BRIGHTNESS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_CONTRAST)) {
+ pictureParams.add(PictureParameter.contrast(params.getInt(
+ PictureQuality.PARAMETER_CONTRAST)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_SHARPNESS)) {
+ pictureParams.add(PictureParameter.sharpness(params.getInt(
+ PictureQuality.PARAMETER_SHARPNESS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_SATURATION)) {
+ pictureParams.add(PictureParameter.saturation(params.getInt(
+ PictureQuality.PARAMETER_SATURATION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_HUE)) {
+ pictureParams.add(PictureParameter.hue(params.getInt(
+ PictureQuality.PARAMETER_HUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
+ pictureParams.add(PictureParameter.colorTunerBrightness(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
+ pictureParams.add(PictureParameter.colorTunerSaturation(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
+ pictureParams.add(PictureParameter.colorTunerHue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTunerRedOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTunerGreenOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTunerBlueOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+ pictureParams.add(PictureParameter.colorTunerRedGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+ pictureParams.add(PictureParameter.colorTunerGreenGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+ pictureParams.add(PictureParameter.colorTunerBlueGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
+ pictureParams.add(PictureParameter.noiseReduction(
+ (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
+ pictureParams.add(PictureParameter.mpegNoiseReduction(
+ (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) {
+ pictureParams.add(PictureParameter.fleshTone(
+ (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) {
+ pictureParams.add(PictureParameter.deContour(
+ (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
+ pictureParams.add(PictureParameter.dynamicLumaControl(
+ (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) {
+ pictureParams.add(PictureParameter.filmMode(params.getBoolean(
+ PictureQuality.PARAMETER_FILM_MODE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_BLUE_STRETCH)) {
+ pictureParams.add(PictureParameter.blueStretch(params.getBoolean(
+ PictureQuality.PARAMETER_BLUE_STRETCH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNE)) {
+ pictureParams.add(PictureParameter.colorTune(params.getBoolean(
+ PictureQuality.PARAMETER_COLOR_TUNE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
+ pictureParams.add(PictureParameter.colorTemperature(
+ (byte) params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
+ pictureParams.add(PictureParameter.globeDimming(params.getBoolean(
+ PictureQuality.PARAMETER_GLOBAL_DIMMING)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
+ pictureParams.add(PictureParameter.autoPictureQualityEnabled(params.getBoolean(
+ PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
+ pictureParams.add(PictureParameter.autoSuperResolutionEnabled(params.getBoolean(
+ PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+ pictureParams.add(PictureParameter.colorTemperatureRedGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+ pictureParams.add(PictureParameter.colorTemperatureGreenGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+ pictureParams.add(PictureParameter.colorTemperatureBlueGain(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) {
+ pictureParams.add(PictureParameter.levelRange(
+ (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
+ pictureParams.add(PictureParameter.gamutMapping(params.getBoolean(
+ PictureQuality.PARAMETER_GAMUT_MAPPING)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PC_MODE)) {
+ pictureParams.add(PictureParameter.pcMode(params.getBoolean(
+ PictureQuality.PARAMETER_PC_MODE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_LOW_LATENCY)) {
+ pictureParams.add(PictureParameter.lowLatency(params.getBoolean(
+ PictureQuality.PARAMETER_LOW_LATENCY)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_VRR)) {
+ pictureParams.add(PictureParameter.vrr(params.getBoolean(
+ PictureQuality.PARAMETER_VRR)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_CVRR)) {
+ pictureParams.add(PictureParameter.cvrr(params.getBoolean(
+ PictureQuality.PARAMETER_CVRR)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
+ pictureParams.add(PictureParameter.hdmiRgbRange(
+ (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) {
+ pictureParams.add(PictureParameter.colorSpace(
+ (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) {
+ pictureParams.add(PictureParameter.panelInitMaxLuminceNits(
+ params.getInt(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
+ pictureParams.add(PictureParameter.panelInitMaxLuminceValid(
+ params.getBoolean(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) {
+ pictureParams.add(PictureParameter.gamma(
+ (byte) params.getInt(PictureQuality.PARAMETER_GAMMA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTemperatureRedOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTemperatureGreenOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
+ pictureParams.add(PictureParameter.colorTemperatureBlueOffset(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
+ pictureParams.add(PictureParameter.elevenPointRed(params.getIntArray(
+ PictureQuality.PARAMETER_ELEVEN_POINT_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
+ pictureParams.add(PictureParameter.elevenPointGreen(params.getIntArray(
+ PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
+ pictureParams.add(PictureParameter.elevenPointBlue(params.getIntArray(
+ PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
+ pictureParams.add(PictureParameter.lowBlueLight(
+ (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) {
+ pictureParams.add(PictureParameter.LdMode(
+ (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
+ pictureParams.add(PictureParameter.osdRedGain(params.getInt(
+ PictureQuality.PARAMETER_OSD_RED_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
+ pictureParams.add(PictureParameter.osdGreenGain(params.getInt(
+ PictureQuality.PARAMETER_OSD_GREEN_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
+ pictureParams.add(PictureParameter.osdBlueGain(params.getInt(
+ PictureQuality.PARAMETER_OSD_BLUE_GAIN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
+ pictureParams.add(PictureParameter.osdRedOffset(params.getInt(
+ PictureQuality.PARAMETER_OSD_RED_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
+ pictureParams.add(PictureParameter.osdGreenOffset(params.getInt(
+ PictureQuality.PARAMETER_OSD_GREEN_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
+ pictureParams.add(PictureParameter.osdBlueOffset(params.getInt(
+ PictureQuality.PARAMETER_OSD_BLUE_OFFSET)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_HUE)) {
+ pictureParams.add(PictureParameter.osdHue(params.getInt(
+ PictureQuality.PARAMETER_OSD_HUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_SATURATION)) {
+ pictureParams.add(PictureParameter.osdSaturation(params.getInt(
+ PictureQuality.PARAMETER_OSD_SATURATION)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_OSD_CONTRAST)) {
+ pictureParams.add(PictureParameter.osdContrast(params.getInt(
+ PictureQuality.PARAMETER_OSD_CONTRAST)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
+ pictureParams.add(PictureParameter.colorTunerSwitch(params.getBoolean(
+ PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
+ pictureParams.add(PictureParameter.colorTunerHueRed(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
+ pictureParams.add(PictureParameter.colorTunerHueGreen(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
+ pictureParams.add(PictureParameter.colorTunerHueBlue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
+ pictureParams.add(PictureParameter.colorTunerHueCyan(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
+ pictureParams.add(PictureParameter.colorTunerHueMagenta(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
+ pictureParams.add(PictureParameter.colorTunerHueYellow(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
+ pictureParams.add(PictureParameter.colorTunerHueFlesh(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationRed(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationGreen(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationBlue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationCyan(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationMagenta(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationYellow(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
+ pictureParams.add(PictureParameter.colorTunerSaturationFlesh(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceRed(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceGreen(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceBlue(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceCyan(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceMagenta(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceYellow(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
+ pictureParams.add(PictureParameter.colorTunerLuminanceFlesh(params.getInt(
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)));
+ }
+ if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) {
+ pictureParams.add(PictureParameter.pictureQualityEventType(
+ (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)));
+ }
+ return (PictureParameter[]) pictureParams.toArray();
+ }
+
+ /**
+ * Convert SoundParameter List to PersistableBundle.
+ */
+ public static PersistableBundle convertSoundParameterListToPersistableBundle(
+ SoundParameter[] parameters) {
+ if (parameters == null) {
+ return null;
+ }
+
+ PersistableBundle bundle = new PersistableBundle();
+ for (SoundParameter sp: parameters) {
+ if (sp.getSurroundSoundEnabled()) {
+ bundle.putBoolean(SoundQuality.PARAMETER_SURROUND_SOUND, true);
+ }
+ if (sp.getSpeakersEnabled()) {
+ bundle.putBoolean(SoundQuality.PARAMETER_SPEAKERS, true);
+ }
+ if (sp.getAutoVolumeControl()) {
+ bundle.putBoolean(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL, true);
+ }
+ if (sp.getDtsDrc()) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_DRC, true);
+ }
+ if (sp.getSurroundSoundEnabled()) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS, true);
+ }
+ if (sp.getEnhancedAudioReturnChannelEnabled()) {
+ bundle.putBoolean(SoundQuality.PARAMETER_EARC, true);
+ }
+ if (sp.getBalance() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_BALANCE, sp.getBalance());
+ }
+ if (sp.getBass() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_BASS, sp.getBass());
+ }
+ if (sp.getTreble() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_TREBLE, sp.getTreble());
+ }
+ if (sp.getSpeakersDelayMs() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS,
+ sp.getSpeakersDelayMs());
+ }
+ if (sp.getDownmixMode() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_DOWN_MIX_MODE, sp.getDownmixMode());
+ }
+ if (sp.getSoundStyle() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_SOUND_STYLE, sp.getSoundStyle());
+ }
+ if (sp.getDigitalOutput() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE,
+ sp.getDigitalOutput());
+ }
+ if (sp.getDolbyDialogueEnhancer() > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_DIALOGUE_ENHANCER,
+ sp.getDolbyDialogueEnhancer());
+ }
+ if (sp.getDtsVirtualX().tbHdx) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TBHDX, true);
+ }
+ if (sp.getDtsVirtualX().limiter) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_LIMITER, true);
+ }
+ if (sp.getDtsVirtualX().truSurroundX) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X, true);
+ }
+ if (sp.getDtsVirtualX().truVolumeHd) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD, true);
+ }
+ if (sp.getDtsVirtualX().dialogClarity) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY, true);
+ }
+ if (sp.getDtsVirtualX().definition) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DEFINITION, true);
+ }
+ if (sp.getDtsVirtualX().height) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_HEIGHT, true);
+ }
+ if (sp.getDolbyAudioProcessing().soundMode > -1) {
+ bundle.putInt(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE,
+ sp.getDolbyAudioProcessing().soundMode);
+ }
+ if (sp.getDolbyAudioProcessing().volumeLeveler) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER,
+ true);
+ }
+ if (sp.getDolbyAudioProcessing().surroundVirtualizer) {
+ bundle.putBoolean(
+ SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER,
+ true);
+ }
+ if (sp.getDolbyAudioProcessing().dolbyAtmos) {
+ bundle.putBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS,
+ true);
+ }
+ }
+ return bundle;
+ }
+ /**
+ * Convert PersistableBundle to SoundParameter List.
+ */
+ public static SoundParameter[] convertPersistableBundleToSoundParameterList(
+ PersistableBundle params) {
+ //TODO: set EqualizerDetail
+ List<SoundParameter> soundParams = new ArrayList<>();
+ if (params.containsKey(SoundQuality.PARAMETER_BALANCE)) {
+ soundParams.add(SoundParameter.balance(params.getInt(
+ SoundQuality.PARAMETER_BALANCE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_BASS)) {
+ soundParams.add(SoundParameter.bass(params.getInt(SoundQuality.PARAMETER_BASS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_TREBLE)) {
+ soundParams.add(SoundParameter.treble(params.getInt(
+ SoundQuality.PARAMETER_TREBLE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SURROUND_SOUND)) {
+ soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_SURROUND_SOUND)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS)) {
+ soundParams.add(SoundParameter.speakersEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_SPEAKERS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
+ soundParams.add(SoundParameter.speakersDelayMs(params.getInt(
+ SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
+ soundParams.add(SoundParameter.autoVolumeControl(params.getBoolean(
+ SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DTS_DRC)) {
+ soundParams.add(SoundParameter.dtsDrc(params.getBoolean(
+ SoundQuality.PARAMETER_DTS_DRC)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
+ soundParams.add(SoundParameter.surroundSoundEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_EARC)) {
+ soundParams.add(SoundParameter.enhancedAudioReturnChannelEnabled(params.getBoolean(
+ SoundQuality.PARAMETER_EARC)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DOWN_MIX_MODE)) {
+ soundParams.add(SoundParameter.downmixMode((byte) params.getInt(
+ SoundQuality.PARAMETER_DOWN_MIX_MODE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_SOUND_STYLE)) {
+ soundParams.add(SoundParameter.soundStyle((byte) params.getInt(
+ SoundQuality.PARAMETER_SOUND_STYLE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)) {
+ soundParams.add(SoundParameter.digitalOutput((byte) params.getInt(
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)));
+ }
+ if (params.containsKey(SoundQuality.PARAMETER_DIALOGUE_ENHANCER)) {
+ soundParams.add(SoundParameter.dolbyDialogueEnhancer((byte) params.getInt(
+ SoundQuality.PARAMETER_DIALOGUE_ENHANCER)));
+ }
+
+ DolbyAudioProcessing dab = new DolbyAudioProcessing();
+ dab.soundMode =
+ (byte) params.getInt(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE);
+ dab.volumeLeveler =
+ params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER);
+ dab.surroundVirtualizer = params.getBoolean(
+ SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER);
+ dab.dolbyAtmos =
+ params.getBoolean(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS);
+ soundParams.add(SoundParameter.dolbyAudioProcessing(dab));
+
+ DtsVirtualX dts = new DtsVirtualX();
+ dts.tbHdx = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TBHDX);
+ dts.limiter = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_LIMITER);
+ dts.truSurroundX = params.getBoolean(
+ SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X);
+ dts.truVolumeHd = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD);
+ dts.dialogClarity = params.getBoolean(
+ SoundQuality.PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY);
+ dts.definition = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_DEFINITION);
+ dts.height = params.getBoolean(SoundQuality.PARAMETER_DTS_VIRTUAL_X_HEIGHT);
+ soundParams.add(SoundParameter.dtsVirtualX(dts));
+
+ return (SoundParameter[]) soundParams.toArray();
+ }
+
+ private static String persistableBundleToJson(PersistableBundle bundle) {
+ JSONObject json = new JSONObject();
+ for (String key : bundle.keySet()) {
+ Object value = bundle.get(key);
+ try {
+ if (value instanceof String) {
+ json.put(key, bundle.getString(key));
+ } else if (value instanceof Integer) {
+ json.put(key, bundle.getInt(key));
+ } else if (value instanceof Long) {
+ json.put(key, bundle.getLong(key));
+ } else if (value instanceof Boolean) {
+ json.put(key, bundle.getBoolean(key));
+ } else if (value instanceof Double) {
+ json.put(key, bundle.getDouble(key));
+ }
+ } catch (JSONException e) {
+ Log.e(TAG, "Unable to serialize ", e);
+ }
+ }
+ return json.toString();
+ }
+
+ private static PersistableBundle jsonToPersistableBundle(String jsonString) {
+ PersistableBundle bundle = new PersistableBundle();
+ if (jsonString != null) {
+ JSONObject jsonObject = null;
+ try {
+ jsonObject = new JSONObject(jsonString);
+
+ Iterator<String> keys = jsonObject.keys();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ Object value = jsonObject.get(key);
+
+ if (value instanceof String) {
+ bundle.putString(key, (String) value);
+ } else if (value instanceof Integer) {
+ bundle.putInt(key, (Integer) value);
+ } else if (value instanceof Boolean) {
+ bundle.putBoolean(key, (Boolean) value);
+ } else if (value instanceof Double) {
+ bundle.putDouble(key, (Double) value);
+ } else if (value instanceof Long) {
+ bundle.putLong(key, (Long) value);
+ }
+ }
+ } catch (JSONException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return bundle;
+ }
+
+ /**
+ * Populates the given map with the ID and generated UUID.
+ */
+ public static void populateTempIdMap(BiMap<Long, String> map, Long id) {
+ if (id != null && map.getValue(id) == null) {
+ String uuid;
+ int attempts = 0;
+ while (attempts < MAX_UUID_GENERATION_ATTEMPTS) {
+ uuid = UUID.randomUUID().toString();
+ if (map.getKey(uuid) == null) {
+ map.put(id, uuid);
+ return;
+ }
+ attempts++;
+ }
+ }
+ }
+
+ /**
+ * Get Content Values.
+ */
+ public static ContentValues getContentValues(Long dbId, Integer profileType, String name,
+ String packageName, String inputId, PersistableBundle params) {
+ ContentValues values = new ContentValues();
+ if (dbId != null) {
+ values.put(BaseParameters.PARAMETER_ID, dbId);
+ }
+ if (profileType != null) {
+ values.put(BaseParameters.PARAMETER_TYPE, profileType);
+ }
+ if (name != null) {
+ values.put(BaseParameters.PARAMETER_NAME, name);
+ }
+ if (packageName != null) {
+ values.put(BaseParameters.PARAMETER_PACKAGE, packageName);
+ }
+ if (inputId != null) {
+ values.put(BaseParameters.PARAMETER_INPUT_ID, inputId);
+ }
+ if (params != null) {
+ values.put(SETTINGS, persistableBundleToJson(params));
+ }
+ return values;
+ }
+
+ /**
+ * Get Media Profile Columns.
+ */
+ public static String[] getMediaProfileColumns(boolean includeParams) {
+ ArrayList<String> columns = new ArrayList<>(Arrays.asList(
+ BaseParameters.PARAMETER_ID,
+ BaseParameters.PARAMETER_TYPE,
+ BaseParameters.PARAMETER_NAME,
+ BaseParameters.PARAMETER_INPUT_ID,
+ BaseParameters.PARAMETER_PACKAGE)
+ );
+ if (includeParams) {
+ columns.add(SETTINGS);
+ }
+ return columns.toArray(new String[0]);
+ }
+
+ /**
+ * Convert cursor to Picture Profile with temporary UUID.
+ */
+ public static PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor,
+ BiMap<Long, String> map) {
+ return new PictureProfile(
+ getTempId(map, cursor),
+ getType(cursor),
+ getName(cursor),
+ getInputId(cursor),
+ getPackageName(cursor),
+ jsonToPersistableBundle(getSettingsString(cursor)),
+ PictureProfileHandle.NONE
+ );
+ }
+
+ /**
+ * Convert cursor to Sound Profile with temporary UUID.
+ */
+ public static SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor, BiMap<Long,
+ String> map) {
+ return new SoundProfile(
+ getTempId(map, cursor),
+ getType(cursor),
+ getName(cursor),
+ getInputId(cursor),
+ getPackageName(cursor),
+ jsonToPersistableBundle(getSettingsString(cursor)),
+ SoundProfileHandle.NONE
+ );
+ }
+
+ /**
+ * Convert parameter to byte array.
+ */
+ public static byte[] convertParameterToByteArray(List<String> names) {
+ /**
+ * TODO Add following to ParameterName & add conversion here.
+ * - PICTURE_QUALITY_EVENT_TYPE
+ * - PANEL_INIT_MAX_LUMINCE_NITS
+ */
+
+ HashSet<String> nameMap = new HashSet<>(names);
+
+ List<Byte> bytes = new ArrayList<>();
+ // Picture Quality parameters
+ if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
+ bytes.add(ParameterName.BRIGHTNESS);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
+ bytes.add(ParameterName.BRIGHTNESS);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_CONTRAST)) {
+ bytes.add(ParameterName.CONTRAST);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_SHARPNESS)) {
+ bytes.add(ParameterName.SHARPNESS);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_SATURATION)) {
+ bytes.add(ParameterName.SATURATION);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_HUE)) {
+ bytes.add(ParameterName.HUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_BRIGHTNESS)) {
+ bytes.add(ParameterName.BRIGHTNESS);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS)) {
+ bytes.add(ParameterName.COLOR_TUNER_BRIGHTNESS);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_SATURATION)) {
+ bytes.add(ParameterName.SATURATION);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_HUE)) {
+ bytes.add(ParameterName.HUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET)) {
+ bytes.add(ParameterName.COLOR_TUNER_RED_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET)) {
+ bytes.add(ParameterName.COLOR_TUNER_GREEN_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET)) {
+ bytes.add(ParameterName.COLOR_TUNER_BLUE_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN)) {
+ bytes.add(ParameterName.COLOR_TUNER_RED_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN)) {
+ bytes.add(ParameterName.COLOR_TUNER_GREEN_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN)) {
+ bytes.add(ParameterName.COLOR_TUNER_BLUE_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_NOISE_REDUCTION)) {
+ bytes.add(ParameterName.NOISE_REDUCTION);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) {
+ bytes.add(ParameterName.MPEG_NOISE_REDUCTION);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_FLESH_TONE)) {
+ bytes.add(ParameterName.FLASH_TONE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_DECONTOUR)) {
+ bytes.add(ParameterName.DE_CONTOUR);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) {
+ bytes.add(ParameterName.DYNAMIC_LUMA_CONTROL);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_FILM_MODE)) {
+ bytes.add(ParameterName.FILM_MODE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_BLUE_STRETCH)) {
+ bytes.add(ParameterName.BLUE_STRETCH);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNE)) {
+ bytes.add(ParameterName.COLOR_TUNE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_GLOBAL_DIMMING)) {
+ bytes.add(ParameterName.GLOBE_DIMMING);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED)) {
+ bytes.add(ParameterName.AUTO_PICTUREQUALITY_ENABLED);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED)) {
+ bytes.add(ParameterName.AUTO_SUPER_RESOLUTION_ENABLED);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_LEVEL_RANGE)) {
+ bytes.add(ParameterName.LEVEL_RANGE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_GAMUT_MAPPING)) {
+ bytes.add(ParameterName.GAMUT_MAPPING);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_PC_MODE)) {
+ bytes.add(ParameterName.PC_MODE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_LOW_LATENCY)) {
+ bytes.add(ParameterName.LOW_LATENCY);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_VRR)) {
+ bytes.add(ParameterName.VRR);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_CVRR)) {
+ bytes.add(ParameterName.CVRR);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) {
+ bytes.add(ParameterName.HDMI_RGB_RANGE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_SPACE)) {
+ bytes.add(ParameterName.COLOR_SPACE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID)) {
+ bytes.add(ParameterName.PANEL_INIT_MAX_LUMINCE_VALID);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_GAMMA)) {
+ bytes.add(ParameterName.GAMMA);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE_RED_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE_GREEN_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET)) {
+ bytes.add(ParameterName.COLOR_TEMPERATURE_BLUE_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_ELEVEN_POINT_RED)) {
+ bytes.add(ParameterName.ELEVEN_POINT_RED);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_ELEVEN_POINT_GREEN)) {
+ bytes.add(ParameterName.ELEVEN_POINT_GREEN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE)) {
+ bytes.add(ParameterName.ELEVEN_POINT_BLUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) {
+ bytes.add(ParameterName.LOW_BLUE_LIGHT);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_LD_MODE)) {
+ bytes.add(ParameterName.LD_MODE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_RED_GAIN)) {
+ bytes.add(ParameterName.OSD_RED_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_GREEN_GAIN)) {
+ bytes.add(ParameterName.OSD_GREEN_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_BLUE_GAIN)) {
+ bytes.add(ParameterName.OSD_BLUE_GAIN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_RED_OFFSET)) {
+ bytes.add(ParameterName.OSD_RED_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_GREEN_OFFSET)) {
+ bytes.add(ParameterName.OSD_GREEN_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_BLUE_OFFSET)) {
+ bytes.add(ParameterName.OSD_BLUE_OFFSET);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_HUE)) {
+ bytes.add(ParameterName.OSD_HUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_SATURATION)) {
+ bytes.add(ParameterName.OSD_SATURATION);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_OSD_CONTRAST)) {
+ bytes.add(ParameterName.OSD_CONTRAST);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SWITCH)) {
+ bytes.add(ParameterName.COLOR_TUNER_SWITCH);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_RED);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_GREEN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_BLUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_CYAN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_MAGENTA);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_YELLOW);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH)) {
+ bytes.add(ParameterName.COLOR_TUNER_HUE_FLESH);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_RED);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_GREEN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_BLUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_CYAN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_MAGENTA);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_YELLOW);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH)) {
+ bytes.add(ParameterName.COLOR_TUNER_SATURATION_FLESH);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_RED);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_GREEN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_BLUE);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_CYAN);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_MAGENTA);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_YELLOW);
+ }
+ if (nameMap.contains(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH)) {
+ bytes.add(ParameterName.COLOR_TUNER_LUMINANCE_FLESH);
+ }
+
+ // Sound Quality parameters
+ if (nameMap.contains(SoundQuality.PARAMETER_BALANCE)) {
+ bytes.add(ParameterName.BALANCE);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_BASS)) {
+ bytes.add(ParameterName.BASS);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_TREBLE)) {
+ bytes.add(ParameterName.TREBLE);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_SURROUND_SOUND)) {
+ bytes.add(ParameterName.SURROUND_SOUND_ENABLED);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_EQUALIZER_DETAIL)) {
+ bytes.add(ParameterName.EQUALIZER_DETAIL);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_SPEAKERS)) {
+ bytes.add(ParameterName.SPEAKERS_ENABLED);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS)) {
+ bytes.add(ParameterName.SPEAKERS_DELAY_MS);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_EARC)) {
+ bytes.add(ParameterName.ENHANCED_AUDIO_RETURN_CHANNEL_ENABLED);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL)) {
+ bytes.add(ParameterName.AUTO_VOLUME_CONTROL);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DOWN_MIX_MODE)) {
+ bytes.add(ParameterName.DOWNMIX_MODE);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DTS_DRC)) {
+ bytes.add(ParameterName.DTS_DRC);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING)) {
+ bytes.add(ParameterName.DOLBY_AUDIO_PROCESSING);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DIALOGUE_ENHANCER)) {
+ bytes.add(ParameterName.DOLBY_DIALOGUE_ENHANCER);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DTS_VIRTUAL_X)) {
+ bytes.add(ParameterName.DTS_VIRTUAL_X);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS)) {
+ bytes.add(ParameterName.DIGITAL_OUTPUT_DELAY_MS);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE)) {
+ bytes.add(ParameterName.DIGITAL_OUTPUT);
+ }
+ if (nameMap.contains(SoundQuality.PARAMETER_SOUND_STYLE)) {
+ bytes.add(ParameterName.SOUND_STYLE);
+ }
+
+ byte[] byteArray = new byte[bytes.size()];
+ for (int i = 0; i < bytes.size(); i++) {
+ byteArray[i] = bytes.get(i);
+ }
+ return byteArray;
+ }
+
+ /**
+ * Get Parameter Name based on byte.
+ */
+ public static String getParameterName(byte pn) {
+ Map<Byte, String> parameterNameMap = new HashMap<>();
+ parameterNameMap.put(ParameterName.BRIGHTNESS, PictureQuality.PARAMETER_BRIGHTNESS);
+ parameterNameMap.put(ParameterName.CONTRAST, PictureQuality.PARAMETER_CONTRAST);
+ parameterNameMap.put(ParameterName.SHARPNESS, PictureQuality.PARAMETER_SHARPNESS);
+ parameterNameMap.put(ParameterName.SATURATION, PictureQuality.PARAMETER_SATURATION);
+ parameterNameMap.put(ParameterName.HUE, PictureQuality.PARAMETER_HUE);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_BRIGHTNESS,
+ PictureQuality.PARAMETER_COLOR_TUNER_BRIGHTNESS);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_RED_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_OFFSET);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_GREEN_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_OFFSET);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_BLUE_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_OFFSET);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_RED_GAIN,
+ PictureQuality.PARAMETER_COLOR_TUNER_RED_GAIN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_GREEN_GAIN,
+ PictureQuality.PARAMETER_COLOR_TUNER_GREEN_GAIN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_BLUE_GAIN,
+ PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN);
+ parameterNameMap.put(ParameterName.NOISE_REDUCTION,
+ PictureQuality.PARAMETER_NOISE_REDUCTION);
+ parameterNameMap.put(ParameterName.MPEG_NOISE_REDUCTION,
+ PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION);
+ parameterNameMap.put(ParameterName.FLASH_TONE, PictureQuality.PARAMETER_FLESH_TONE);
+ parameterNameMap.put(ParameterName.DE_CONTOUR, PictureQuality.PARAMETER_DECONTOUR);
+ parameterNameMap.put(ParameterName.DYNAMIC_LUMA_CONTROL,
+ PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL);
+ parameterNameMap.put(ParameterName.FILM_MODE,
+ PictureQuality.PARAMETER_FILM_MODE);
+ parameterNameMap.put(ParameterName.BLACK_STRETCH,
+ PictureQuality.PARAMETER_BLACK_STRETCH);
+ parameterNameMap.put(ParameterName.BLUE_STRETCH,
+ PictureQuality.PARAMETER_BLUE_STRETCH);
+ parameterNameMap.put(ParameterName.COLOR_TUNE,
+ PictureQuality.PARAMETER_COLOR_TUNE);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE);
+ parameterNameMap.put(ParameterName.GLOBE_DIMMING,
+ PictureQuality.PARAMETER_GLOBAL_DIMMING);
+ parameterNameMap.put(ParameterName.AUTO_PICTUREQUALITY_ENABLED,
+ PictureQuality.PARAMETER_AUTO_PICTURE_QUALITY_ENABLED);
+ parameterNameMap.put(ParameterName.AUTO_SUPER_RESOLUTION_ENABLED,
+ PictureQuality.PARAMETER_AUTO_SUPER_RESOLUTION_ENABLED);
+ parameterNameMap.put(ParameterName.LEVEL_RANGE, PictureQuality.PARAMETER_LEVEL_RANGE);
+ parameterNameMap.put(ParameterName.GAMUT_MAPPING,
+ PictureQuality.PARAMETER_GAMUT_MAPPING);
+ parameterNameMap.put(ParameterName.PC_MODE, PictureQuality.PARAMETER_PC_MODE);
+ parameterNameMap.put(ParameterName.LOW_LATENCY, PictureQuality.PARAMETER_LOW_LATENCY);
+ parameterNameMap.put(ParameterName.VRR, PictureQuality.PARAMETER_VRR);
+ parameterNameMap.put(ParameterName.CVRR, PictureQuality.PARAMETER_CVRR);
+ parameterNameMap.put(ParameterName.HDMI_RGB_RANGE,
+ PictureQuality.PARAMETER_HDMI_RGB_RANGE);
+ parameterNameMap.put(ParameterName.COLOR_SPACE, PictureQuality.PARAMETER_COLOR_SPACE);
+ parameterNameMap.put(ParameterName.PANEL_INIT_MAX_LUMINCE_VALID,
+ PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID);
+ parameterNameMap.put(ParameterName.GAMMA, PictureQuality.PARAMETER_GAMMA);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_RED_GAIN,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_GAIN);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_GREEN_GAIN,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_GAIN);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_BLUE_GAIN,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_GAIN);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_RED_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_GREEN_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_GREEN_OFFSET);
+ parameterNameMap.put(ParameterName.COLOR_TEMPERATURE_BLUE_OFFSET,
+ PictureQuality.PARAMETER_COLOR_TEMPERATURE_BLUE_OFFSET);
+ parameterNameMap.put(ParameterName.ELEVEN_POINT_RED,
+ PictureQuality.PARAMETER_ELEVEN_POINT_RED);
+ parameterNameMap.put(ParameterName.ELEVEN_POINT_GREEN,
+ PictureQuality.PARAMETER_ELEVEN_POINT_GREEN);
+ parameterNameMap.put(ParameterName.ELEVEN_POINT_BLUE,
+ PictureQuality.PARAMETER_ELEVEN_POINT_BLUE);
+ parameterNameMap.put(ParameterName.LOW_BLUE_LIGHT,
+ PictureQuality.PARAMETER_LOW_BLUE_LIGHT);
+ parameterNameMap.put(ParameterName.LD_MODE, PictureQuality.PARAMETER_LD_MODE);
+ parameterNameMap.put(ParameterName.OSD_RED_GAIN, PictureQuality.PARAMETER_OSD_RED_GAIN);
+ parameterNameMap.put(ParameterName.OSD_GREEN_GAIN,
+ PictureQuality.PARAMETER_OSD_GREEN_GAIN);
+ parameterNameMap.put(ParameterName.OSD_BLUE_GAIN,
+ PictureQuality.PARAMETER_OSD_BLUE_GAIN);
+ parameterNameMap.put(ParameterName.OSD_RED_OFFSET,
+ PictureQuality.PARAMETER_OSD_RED_OFFSET);
+ parameterNameMap.put(ParameterName.OSD_GREEN_OFFSET,
+ PictureQuality.PARAMETER_OSD_GREEN_OFFSET);
+ parameterNameMap.put(ParameterName.OSD_BLUE_OFFSET,
+ PictureQuality.PARAMETER_OSD_BLUE_OFFSET);
+ parameterNameMap.put(ParameterName.OSD_HUE, PictureQuality.PARAMETER_OSD_HUE);
+ parameterNameMap.put(ParameterName.OSD_SATURATION,
+ PictureQuality.PARAMETER_OSD_SATURATION);
+ parameterNameMap.put(ParameterName.OSD_CONTRAST,
+ PictureQuality.PARAMETER_OSD_CONTRAST);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SWITCH,
+ PictureQuality.PARAMETER_COLOR_TUNER_SWITCH);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_RED,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_RED);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_GREEN,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_GREEN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_BLUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_BLUE);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_CYAN,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_CYAN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_MAGENTA,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_MAGENTA);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_YELLOW,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_YELLOW);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_HUE_FLESH,
+ PictureQuality.PARAMETER_COLOR_TUNER_HUE_FLESH);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_RED,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_RED);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_GREEN,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_GREEN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_BLUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_BLUE);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_CYAN,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_CYAN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_MAGENTA,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_MAGENTA);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_YELLOW,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_YELLOW);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_SATURATION_FLESH,
+ PictureQuality.PARAMETER_COLOR_TUNER_SATURATION_FLESH);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_RED,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_RED);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_GREEN,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_GREEN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_BLUE,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_BLUE);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_CYAN,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_CYAN);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_MAGENTA,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_MAGENTA);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_YELLOW,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_YELLOW);
+ parameterNameMap.put(ParameterName.COLOR_TUNER_LUMINANCE_FLESH,
+ PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH);
+ parameterNameMap.put(ParameterName.BALANCE, SoundQuality.PARAMETER_BALANCE);
+ parameterNameMap.put(ParameterName.BASS, SoundQuality.PARAMETER_BASS);
+ parameterNameMap.put(ParameterName.TREBLE, SoundQuality.PARAMETER_TREBLE);
+ parameterNameMap.put(ParameterName.SURROUND_SOUND_ENABLED,
+ SoundQuality.PARAMETER_SURROUND_SOUND);
+ parameterNameMap.put(ParameterName.EQUALIZER_DETAIL,
+ SoundQuality.PARAMETER_EQUALIZER_DETAIL);
+ parameterNameMap.put(ParameterName.SPEAKERS_ENABLED, SoundQuality.PARAMETER_SPEAKERS);
+ parameterNameMap.put(ParameterName.SPEAKERS_DELAY_MS,
+ SoundQuality.PARAMETER_SPEAKERS_DELAY_MILLIS);
+ parameterNameMap.put(ParameterName.ENHANCED_AUDIO_RETURN_CHANNEL_ENABLED,
+ SoundQuality.PARAMETER_EARC);
+ parameterNameMap.put(ParameterName.AUTO_VOLUME_CONTROL,
+ SoundQuality.PARAMETER_AUTO_VOLUME_CONTROL);
+ parameterNameMap.put(ParameterName.DOWNMIX_MODE, SoundQuality.PARAMETER_DOWN_MIX_MODE);
+ parameterNameMap.put(ParameterName.DTS_DRC, SoundQuality.PARAMETER_DTS_DRC);
+ parameterNameMap.put(ParameterName.DOLBY_AUDIO_PROCESSING,
+ SoundQuality.PARAMETER_DOLBY_AUDIO_PROCESSING);
+ parameterNameMap.put(ParameterName.DOLBY_DIALOGUE_ENHANCER,
+ SoundQuality.PARAMETER_DIALOGUE_ENHANCER);
+ parameterNameMap.put(ParameterName.DTS_VIRTUAL_X,
+ SoundQuality.PARAMETER_DTS_VIRTUAL_X);
+ parameterNameMap.put(ParameterName.DIGITAL_OUTPUT,
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_MODE);
+ parameterNameMap.put(ParameterName.DIGITAL_OUTPUT_DELAY_MS,
+ SoundQuality.PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS);
+ parameterNameMap.put(ParameterName.SOUND_STYLE, SoundQuality.PARAMETER_SOUND_STYLE);
+
+ return parameterNameMap.get(pn);
+ }
+
+ private static String getTempId(BiMap<Long, String> map, Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_ID);
+ Long dbId = colIndex != -1 ? cursor.getLong(colIndex) : null;
+ populateTempIdMap(map, dbId);
+ return map.getValue(dbId);
+ }
+
+ private static int getType(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_TYPE);
+ return colIndex != -1 ? cursor.getInt(colIndex) : 0;
+ }
+
+ private static String getName(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_NAME);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private static String getInputId(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_INPUT_ID);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private static String getPackageName(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(BaseParameters.PARAMETER_PACKAGE);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private static String getSettingsString(Cursor cursor) {
+ int colIndex = cursor.getColumnIndex(SETTINGS);
+ return colIndex != -1 ? cursor.getString(colIndex) : null;
+ }
+
+ private MediaQualityUtils() {
+
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 0ed5228..a80b1b2 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -171,11 +171,11 @@
void onProposedRotationChanged(int displayId, int rotation, boolean isValid);
/**
- * Notifies System UI that the display is ready to show system decorations.
+ * Notifies System UI that the system decorations should be added on the display.
*
* @param displayId display ID
*/
- void onDisplayReady(int displayId);
+ void onDisplayAddSystemDecorations(int displayId);
/**
* Notifies System UI that the system decorations should be removed from the display.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index e753f27..c546388 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -87,7 +87,6 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.IndentingPrintWriter;
-import android.util.IntArray;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -125,7 +124,6 @@
import com.android.server.power.ShutdownCheckPoints;
import com.android.server.power.ShutdownThread;
import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.systemui.shared.Flags;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -343,19 +341,15 @@
@Override
public void onDisplayAdded(int displayId) {
- if (Flags.statusBarConnectedDisplays()) {
- synchronized (mLock) {
- mDisplayUiState.put(displayId, new UiState());
- }
+ synchronized (mLock) {
+ mDisplayUiState.put(displayId, new UiState());
}
}
@Override
public void onDisplayRemoved(int displayId) {
- if (Flags.statusBarConnectedDisplays()) {
- synchronized (mLock) {
- mDisplayUiState.remove(displayId);
- }
+ synchronized (mLock) {
+ mDisplayUiState.remove(displayId);
}
}
@@ -776,10 +770,11 @@
}
@Override
- public void onDisplayReady(int displayId) {
+ public void onDisplayAddSystemDecorations(int displayId) {
if (isVisibleBackgroundUserOnDisplay(displayId)) {
if (SPEW) {
- Slog.d(TAG, "Skipping onDisplayReady for visible background user "
+ Slog.d(TAG, "Skipping onDisplayAddSystemDecorations for visible background "
+ + "user "
+ mUserManagerInternal.getUserAssignedToDisplay(displayId));
}
return;
@@ -787,7 +782,7 @@
IStatusBar bar = mBar;
if (bar != null) {
try {
- bar.onDisplayReady(displayId);
+ bar.onDisplayAddSystemDecorations(displayId);
} catch (RemoteException ex) {}
}
}
@@ -1366,66 +1361,53 @@
return mTracingEnabled;
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
@Override
public void disable(int what, IBinder token, String pkg) {
disableForUser(what, token, pkg, mCurrentUserId);
}
- /**
- * Disable additional status bar features for user for all displays. Pass the bitwise-or of the
- * {@code #DISABLE_*} flags. To re-enable everything, pass {@code #DISABLE_NONE}.
- *
- * Warning: Only pass {@code #DISABLE_*} flags into this function, do not use
- * {@code #DISABLE2_*} flags.
- */
+ // TODO(b/117478341): make it aware of multi-display if needed.
@Override
public void disableForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
enforceValidCallingUser();
synchronized (mLock) {
- IntArray displayIds = new IntArray();
- for (int i = 0; i < mDisplayUiState.size(); i++) {
- displayIds.add(mDisplayUiState.keyAt(i));
- }
- disableLocked(displayIds, userId, what, token, pkg, 1);
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1);
}
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
/**
- * Disable additional status bar features. Pass the bitwise-or of the {@code #DISABLE2_*} flags.
- * To re-enable everything, pass {@code #DISABLE2_NONE}.
+ * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags.
+ * To re-enable everything, pass {@link #DISABLE2_NONE}.
*
- * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use
- * {@code #DISABLE_*} flags.
+ * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
*/
@Override
public void disable2(int what, IBinder token, String pkg) {
disable2ForUser(what, token, pkg, mCurrentUserId);
}
+ // TODO(b/117478341): make it aware of multi-display if needed.
/**
- * Disable additional status bar features for a given user for all displays. Pass the bitwise-or
- * of the {@code #DISABLE2_*} flags. To re-enable everything, pass {@code #DISABLE2_NONE}.
+ * Disable additional status bar features for a given user. Pass the bitwise-or of the
+ * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}.
*
- * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use
- * {@code #DISABLE_*} flags.
+ * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags.
*/
@Override
public void disable2ForUser(int what, IBinder token, String pkg, int userId) {
enforceStatusBar();
synchronized (mLock) {
- IntArray displayIds = new IntArray();
- for (int i = 0; i < mDisplayUiState.size(); i++) {
- displayIds.add(mDisplayUiState.keyAt(i));
- }
- disableLocked(displayIds, userId, what, token, pkg, 2);
+ disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2);
}
}
- private void disableLocked(IntArray displayIds, int userId, int what, IBinder token,
- String pkg, int whichFlag) {
+ private void disableLocked(int displayId, int userId, int what, IBinder token, String pkg,
+ int whichFlag) {
// It's important that the the callback and the call to mBar get done
// in the same order when multiple threads are calling this function
// so they are paired correctly. The messages on the handler will be
@@ -1435,27 +1417,18 @@
// Ensure state for the current user is applied, even if passed a non-current user.
final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1);
final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2);
- boolean shouldCallNotificationOnSetDisabled = false;
- IStatusBar bar = mBar;
- for (int displayId : displayIds.toArray()) {
- final UiState state = getUiState(displayId);
- if (!state.disableEquals(net1, net2)) {
- shouldCallNotificationOnSetDisabled = true;
- state.setDisabled(net1, net2);
- if (bar != null) {
- try {
- // TODO(b/388244660): Create IStatusBar#disableForAllDisplays to avoid
- // multiple IPC calls.
- bar.disable(displayId, net1, net2);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Unable to disable Status bar.", ex);
- }
+ final UiState state = getUiState(displayId);
+ if (!state.disableEquals(net1, net2)) {
+ state.setDisabled(net1, net2);
+ mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
+ IStatusBar bar = mBar;
+ if (bar != null) {
+ try {
+ bar.disable(displayId, net1, net2);
+ } catch (RemoteException ex) {
}
}
}
- if (shouldCallNotificationOnSetDisabled) {
- mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1));
- }
}
/**
@@ -1610,8 +1583,7 @@
if (SPEW) Slog.d(TAG, "setDisableFlags(0x" + Integer.toHexString(flags) + ")");
synchronized (mLock) {
- disableLocked(IntArray.wrap(new int[]{displayId}), mCurrentUserId, flags,
- mSysUiVisToken, cause, 1);
+ disableLocked(displayId, mCurrentUserId, flags, mSysUiVisToken, cause, 1);
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
index 872ab59..f413fe3 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerInternal.java
@@ -26,7 +26,7 @@
/**
* Notifies the display is ready for adding wallpaper on it.
*/
- public abstract void onDisplayReady(int displayId);
+ public abstract void onDisplayAddSystemDecorations(int displayId);
/** Notifies when display stop showing system decorations and wallpaper. */
public abstract void onDisplayRemoveSystemDecorations(int displayId);
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index db530e7..09b1073 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -1636,8 +1636,8 @@
private final class LocalService extends WallpaperManagerInternal {
@Override
- public void onDisplayReady(int displayId) {
- onDisplayReadyInternal(displayId);
+ public void onDisplayAddSystemDecorations(int displayId) {
+ onDisplayAddSystemDecorationsInternal(displayId);
}
@Override
@@ -3944,7 +3944,7 @@
return (wallpaper != null) ? wallpaper.allowBackup : false;
}
- private void onDisplayReadyInternal(int displayId) {
+ private void onDisplayAddSystemDecorationsInternal(int displayId) {
synchronized (mLock) {
if (mLastWallpaper == null) {
return;
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index c7d4467..6f76618 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -3655,7 +3655,7 @@
private static String getIntentRedirectPreventedLogMessage(@NonNull String message,
@NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
int callingUid, @Nullable String callingPackage) {
- return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid
+ return "[IntentRedirect Hardening] " + message + " intentCreatorUid: " + intentCreatorUid
+ "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
+ "; callingPackage: " + callingPackage + "; intent: " + intent;
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index cf111cd..ddb9f17 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -2988,37 +2988,44 @@
throw new SecurityException("Requires permission "
+ android.Manifest.permission.DEVICE_POWER);
}
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ setLockScreenShownLocked(keyguardShowing, aodShowing);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
- synchronized (mGlobalLock) {
- final long ident = Binder.clearCallingIdentity();
- if (mKeyguardShown != keyguardShowing) {
- mKeyguardShown = keyguardShowing;
- final Message msg = PooledLambda.obtainMessage(
- ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
- keyguardShowing);
- mH.sendMessage(msg);
+ @GuardedBy("mGlobalLock")
+ void setLockScreenShownLocked(boolean keyguardShowing, boolean aodShowing) {
+ if (mKeyguardShown != keyguardShowing) {
+ mKeyguardShown = keyguardShowing;
+ final Message msg = PooledLambda.obtainMessage(
+ ActivityManagerInternal::reportCurKeyguardUsageEvent, mAmInternal,
+ keyguardShowing);
+ mH.sendMessage(msg);
+ }
+ // Always reset the state regardless of keyguard-showing change, because that means the
+ // unlock is either completed or canceled.
+ if ((mDemoteTopAppReasons & DEMOTE_TOP_REASON_DURING_UNLOCKING) != 0) {
+ mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING;
+ // The scheduling group of top process was demoted by unlocking, so recompute
+ // to restore its real top priority if possible.
+ if (mTopApp != null) {
+ mTopApp.scheduleUpdateOomAdj();
}
- // Always reset the state regardless of keyguard-showing change, because that means the
- // unlock is either completed or canceled.
- if ((mDemoteTopAppReasons & DEMOTE_TOP_REASON_DURING_UNLOCKING) != 0) {
- mDemoteTopAppReasons &= ~DEMOTE_TOP_REASON_DURING_UNLOCKING;
- // The scheduling group of top process was demoted by unlocking, so recompute
- // to restore its real top priority if possible.
- if (mTopApp != null) {
- mTopApp.scheduleUpdateOomAdj();
- }
- }
- try {
- Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown");
- mRootWindowContainer.forAllDisplays(displayContent -> {
- mKeyguardController.setKeyguardShown(displayContent.getDisplayId(),
- keyguardShowing, aodShowing);
- });
- maybeHideLockedProfileActivityLocked();
- } finally {
- Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- Binder.restoreCallingIdentity(ident);
- }
+ }
+ try {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "setLockScreenShown");
+ mRootWindowContainer.forAllDisplays(displayContent -> {
+ mKeyguardController.setKeyguardShown(displayContent.getDisplayId(),
+ keyguardShowing, aodShowing);
+ });
+ maybeHideLockedProfileActivityLocked();
+ } finally {
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
mH.post(() -> {
diff --git a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
index ab1778a..15c0789 100644
--- a/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatAspectRatioPolicy.java
@@ -295,9 +295,14 @@
// {@link ActivityRecord#shouldCreateAppCompatDisplayInsets()} will be false for
// both activities that are naturally resizeable and activities that have been
// forced resizeable.
+ // Camera compat mode is an exception to this, where the activity is letterboxed
+ // to an aspect ratio commonly found on phones, e.g. 16:9, to avoid issues like
+ // stretching of the camera preview.
|| (Flags.ignoreAspectRatioRestrictionsForResizeableFreeformActivities()
&& task.getWindowingMode() == WINDOWING_MODE_FREEFORM
- && !mActivityRecord.shouldCreateAppCompatDisplayInsets())) {
+ && !mActivityRecord.shouldCreateAppCompatDisplayInsets()
+ && !AppCompatCameraPolicy.shouldCameraCompatControlAspectRatio(
+ mActivityRecord))) {
return false;
}
diff --git a/services/core/java/com/android/server/wm/DesktopModeHelper.java b/services/core/java/com/android/server/wm/DesktopModeHelper.java
index 6bf1c46..e3906f9 100644
--- a/services/core/java/com/android/server/wm/DesktopModeHelper.java
+++ b/services/core/java/com/android/server/wm/DesktopModeHelper.java
@@ -23,6 +23,7 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
/**
* Constants for desktop mode feature
@@ -35,7 +36,7 @@
"persist.wm.debug.desktop_mode_enforce_device_restrictions", true);
/** Whether desktop mode is enabled. */
- static boolean isDesktopModeEnabled() {
+ private static boolean isDesktopModeEnabled() {
return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_MODE.isTrue();
}
@@ -56,11 +57,30 @@
return context.getResources().getBoolean(R.bool.config_isDesktopModeSupported);
}
+ static boolean isDesktopModeDevOptionsSupported(@NonNull Context context) {
+ return context.getResources().getBoolean(R.bool.config_isDesktopModeDevOptionSupported);
+ }
+
+ /**
+ * Check if Desktop mode should be enabled because the dev option is shown and enabled.
+ */
+ private static boolean isDesktopModeEnabledByDevOption(@NonNull Context context) {
+ return DesktopModeFlags.isDesktopModeForcedEnabled() && (isDesktopModeDevOptionsSupported(
+ context) || isDeviceEligibleForDesktopMode(context));
+ }
+
+ @VisibleForTesting
+ static boolean isDeviceEligibleForDesktopMode(@NonNull Context context) {
+ return !shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context) || (
+ Flags.enableDesktopModeThroughDevOption() && isDesktopModeDevOptionsSupported(
+ context));
+ }
+
/**
* Return {@code true} if desktop mode can be entered on the current device.
*/
static boolean canEnterDesktopMode(@NonNull Context context) {
- return isDesktopModeEnabled()
- && (!shouldEnforceDeviceRestrictions() || isDesktopModeSupported(context));
+ return (isDesktopModeEnabled() && isDeviceEligibleForDesktopMode(context))
+ || isDesktopModeEnabledByDevOption(context);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 5b16199..5329e3b 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1887,17 +1887,17 @@
mCanSystemBarsBeShownByUser = canBeShown;
}
- void notifyDisplayReady() {
+ void notifyDisplayAddSystemDecorations() {
mHandler.post(() -> {
final int displayId = getDisplayId();
StatusBarManagerInternal statusBar = getStatusBarManagerInternal();
if (statusBar != null) {
- statusBar.onDisplayReady(displayId);
+ statusBar.onDisplayAddSystemDecorations(displayId);
}
final WallpaperManagerInternal wpMgr = LocalServices
.getService(WallpaperManagerInternal.class);
if (wpMgr != null) {
- wpMgr.onDisplayReady(displayId);
+ wpMgr.onDisplayAddSystemDecorations(displayId);
}
});
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index abd26b5..cf464c7 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2845,7 +2845,13 @@
}
startHomeOnDisplay(mCurrentUser, reason, displayContent.getDisplayId());
- displayContent.getDisplayPolicy().notifyDisplayReady();
+ if (enableDisplayContentModeManagement()) {
+ if (displayContent.isSystemDecorationsSupported()) {
+ displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
+ }
+ } else {
+ displayContent.getDisplayPolicy().notifyDisplayAddSystemDecorations();
+ }
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 060f2e8..b4c2c01 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -1865,7 +1865,7 @@
if (keyguardState != null) {
boolean keyguardShowing = keyguardState.getKeyguardShowing();
boolean aodShowing = keyguardState.getAodShowing();
- mService.setLockScreenShown(keyguardShowing, aodShowing);
+ mService.setLockScreenShownLocked(keyguardShowing, aodShowing);
}
return effects;
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index e1f3f0e..b37bcc7 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -94,8 +94,6 @@
namespace android {
-static const bool ENABLE_INPUT_FILTER_RUST = input_flags::enable_input_filter_rust_impl();
-
// The exponent used to calculate the pointer speed scaling factor.
// The scaling factor is calculated as 2 ^ (speed * exponent),
// where the speed ranges from -7 to + 7 and is supplied by the user.
@@ -604,12 +602,14 @@
return std::to_string(displayId.val());
};
dump += StringPrintf(INDENT "Display not interactive: %s\n",
- dumpSet(mLocked.nonInteractiveDisplays, streamableToString).c_str());
+ dumpContainer(mLocked.nonInteractiveDisplays, streamableToString)
+ .c_str());
dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
toString(mLocked.systemUiLightsOut));
dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
dump += StringPrintf(INDENT "Display with Mouse Scaling Disabled: %s\n",
- dumpSet(mLocked.displaysWithMouseScalingDisabled, streamableToString)
+ dumpContainer(mLocked.displaysWithMouseScalingDisabled,
+ streamableToString)
.c_str());
dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
toString(mLocked.pointerGesturesEnabled));
@@ -3248,27 +3248,21 @@
static void nativeSetAccessibilityBounceKeysThreshold(JNIEnv* env, jobject nativeImplObj,
jint thresholdTimeMs) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (ENABLE_INPUT_FILTER_RUST) {
- im->getInputManager()->getInputFilter().setAccessibilityBounceKeysThreshold(
- static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
- }
+ im->getInputManager()->getInputFilter().setAccessibilityBounceKeysThreshold(
+ static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
}
static void nativeSetAccessibilitySlowKeysThreshold(JNIEnv* env, jobject nativeImplObj,
jint thresholdTimeMs) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (ENABLE_INPUT_FILTER_RUST) {
- im->getInputManager()->getInputFilter().setAccessibilitySlowKeysThreshold(
- static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
- }
+ im->getInputManager()->getInputFilter().setAccessibilitySlowKeysThreshold(
+ static_cast<nsecs_t>(thresholdTimeMs) * 1000000);
}
static void nativeSetAccessibilityStickyKeysEnabled(JNIEnv* env, jobject nativeImplObj,
jboolean enabled) {
NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
- if (ENABLE_INPUT_FILTER_RUST) {
- im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled);
- }
+ im->getInputManager()->getInputFilter().setAccessibilityStickyKeysEnabled(enabled);
}
static void nativeSetInputMethodConnectionIsActive(JNIEnv* env, jobject nativeImplObj,
diff --git a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
index b92afc5..d80fd20 100644
--- a/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/wallpaper/WallpaperManagerServiceTests.java
@@ -731,7 +731,7 @@
// WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Then there is a connection established for the system & lock wallpaper for display ID, 2.
verify(mockIWallpaperService).attach(
@@ -771,7 +771,7 @@
// WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Then there is a connection established for the system wallpaper for display ID, 2.
verify(mockIWallpaperService).attach(
@@ -818,7 +818,7 @@
// WHEN display ID, 2, is ready.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Then there is a connection established for the fallback wallpaper for display ID, 2.
verify(mockIWallpaperService).attach(
@@ -856,7 +856,7 @@
// GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Save displayConnector for displayId 2 before display removal.
WallpaperManagerService.DisplayConnector displayConnector =
wallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
@@ -894,7 +894,7 @@
// GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Save displayConnectors for display ID, 2, before display removal.
WallpaperManagerService.DisplayConnector systemWallpaperDisplayConnector =
systemWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
@@ -930,7 +930,7 @@
// GIVEN wallpaper connections have been established for display ID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Save fallback wallpaper displayConnector for display ID, 2, before display removal.
WallpaperManagerService.DisplayConnector fallbackWallpaperConnector =
mService.mFallbackWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
@@ -977,7 +977,7 @@
// GIVEN wallpaper connections have been established for displayID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Save displayConnector for displayId 2 before display removal.
WallpaperManagerService.DisplayConnector displayConnector =
wallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
@@ -1011,7 +1011,7 @@
// GIVEN wallpaper connections have been established for displayID, 2.
WallpaperManagerInternal wallpaperManagerInternal = LocalServices.getService(
WallpaperManagerInternal.class);
- wallpaperManagerInternal.onDisplayReady(testDisplayId);
+ wallpaperManagerInternal.onDisplayAddSystemDecorations(testDisplayId);
// Save displayConnectors for displayId 2 before display removal.
WallpaperManagerService.DisplayConnector systemWallpaperDisplayConnector =
systemWallpaper.connection.getDisplayConnectorOrCreate(testDisplayId);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
index 28e5be5..9cfa51a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -29,6 +29,7 @@
import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
import static android.view.accessibility.Flags.FLAG_SKIP_ACCESSIBILITY_WARNING_DIALOG_FOR_TRUSTED_SERVICES;
+import static com.android.input.flags.Flags.FLAG_KEYBOARD_REPEAT_KEYS;
import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.GESTURE;
@@ -38,6 +39,7 @@
import static com.android.internal.accessibility.common.ShortcutConstants.UserShortcutType.SOFTWARE;
import static com.android.internal.accessibility.dialog.AccessibilityButtonChooserActivity.EXTRA_TYPE_TO_CHOOSE;
import static com.android.server.accessibility.AccessibilityManagerService.ACTION_LAUNCH_HEARING_DEVICES_DIALOG;
+import static com.android.server.accessibility.Flags.FLAG_ENABLE_MAGNIFICATION_KEYBOARD_CONTROL;
import static com.google.common.truth.Truth.assertThat;
@@ -93,6 +95,7 @@
import android.os.test.FakePermissionEnforcer;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.Settings;
import android.testing.AndroidTestingRunner;
@@ -584,6 +587,31 @@
}
@Test
+ @RequiresFlagsEnabled({FLAG_ENABLE_MAGNIFICATION_KEYBOARD_CONTROL, FLAG_KEYBOARD_REPEAT_KEYS})
+ public void testRepeatKeysSettingsChanges_propagateToMagnificationController() {
+ final AccessibilityUserState userState = mA11yms.mUserStates.get(
+ mA11yms.getCurrentUserIdLocked());
+ Settings.Secure.putIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_ENABLED,
+ 0, mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.readRepeatKeysSettingsLocked(userState);
+
+ verify(mMockMagnificationController).setRepeatKeysEnabled(false);
+
+ final int timeoutMs = 42;
+ Settings.Secure.putIntForUser(
+ mTestableContext.getContentResolver(),
+ Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
+ timeoutMs, mA11yms.getCurrentUserIdLocked());
+
+ mA11yms.readRepeatKeysSettingsLocked(userState);
+
+ verify(mMockMagnificationController).setRepeatKeysTimeoutMs(timeoutMs);
+ }
+
+ @Test
public void testSettingsAlwaysOn_setEnabled_featureFlagDisabled_doNothing() {
when(mMockMagnificationController.isAlwaysOnMagnificationFeatureFlagEnabled())
.thenReturn(false);
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index d4f2dcc..5d94e72 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -250,9 +250,9 @@
}
@Test
- public void sendGesture_touchableDevice_injectEvents()
- throws RemoteException {
+ public void sendGesture_touchableDevice_injectEvents_fromAccessibilityTool() {
when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
+ when(mServiceInfo.isAccessibilityTool()).thenReturn(true);
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -263,13 +263,31 @@
mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0,
- Display.DEFAULT_DISPLAY);
+ Display.DEFAULT_DISPLAY, true);
+ }
+
+ @Test
+ public void sendGesture_touchableDevice_injectEvents_fromNonTool() {
+ when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(true);
+ when(mServiceInfo.isAccessibilityTool()).thenReturn(false);
+ setServiceBinding(COMPONENT_NAME);
+ mConnection.bindLocked();
+ mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
+
+ ParceledListSlice parceledListSlice = mock(ParceledListSlice.class);
+ List<GestureDescription.GestureStep> gestureSteps = mock(List.class);
+ when(parceledListSlice.getList()).thenReturn(gestureSteps);
+ mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
+
+ verify(mMockMotionEventInjector).injectEvents(gestureSteps, mMockServiceClient, 0,
+ Display.DEFAULT_DISPLAY, false);
}
@Test
public void sendGesture_untouchableDevice_performGestureResultFailed()
throws RemoteException {
when(mMockWindowManagerInternal.isTouchOrFaketouchDevice()).thenReturn(false);
+ when(mServiceInfo.isAccessibilityTool()).thenReturn(true);
setServiceBinding(COMPONENT_NAME);
mConnection.bindLocked();
mConnection.onServiceConnected(COMPONENT_NAME, mMockIBinder);
@@ -280,7 +298,7 @@
mConnection.dispatchGesture(0, parceledListSlice, Display.DEFAULT_DISPLAY);
verify(mMockMotionEventInjector, never()).injectEvents(gestureSteps, mMockServiceClient, 0,
- Display.DEFAULT_DISPLAY);
+ Display.DEFAULT_DISPLAY, true);
verify(mMockServiceClient).onPerformGestureResult(0, false);
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
index 233caf9..d2d8c68 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java
@@ -20,8 +20,7 @@
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_HOVER_MOVE;
import static android.view.MotionEvent.ACTION_UP;
-import static android.view.WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
-import static android.view.WindowManagerPolicyConstants.FLAG_PASS_TO_USER;
+import static android.view.accessibility.Flags.FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS;
import static org.hamcrest.CoreMatchers.allOf;
import static org.hamcrest.CoreMatchers.anyOf;
@@ -48,10 +47,14 @@
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.Display;
import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.WindowManagerPolicyConstants;
import android.view.accessibility.AccessibilityEvent;
import androidx.test.runner.AndroidJUnit4;
@@ -64,6 +67,7 @@
import org.hamcrest.TypeSafeMatcher;
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -77,7 +81,7 @@
*/
@RunWith(AndroidJUnit4.class)
public class MotionEventInjectorTest {
- private static final String LOG_TAG = "MotionEventInjectorTest";
+
private static final Matcher<MotionEvent> IS_ACTION_DOWN =
new MotionEventActionMatcher(ACTION_DOWN);
private static final Matcher<MotionEvent> IS_ACTION_POINTER_DOWN =
@@ -120,6 +124,9 @@
private static final float POINTER_SIZE = 1;
private static final int METASTATE = 0;
+ @Rule
+ public SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
MotionEventInjector mMotionEventInjector;
IAccessibilityServiceClient mServiceInterface;
AccessibilityTraceManager mTrace;
@@ -201,7 +208,8 @@
verifyNoMoreInteractions(next);
mMessageCapturingHandler.sendOneMessage(); // Send a motion event
- final int expectedFlags = FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY;
+ final int expectedFlags = WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY;
verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(), eq(expectedFlags));
verify(next).onMotionEvent(argThat(mIsLineStart), argThat(mIsLineStart), eq(expectedFlags));
verifyNoMoreInteractions(next);
@@ -227,6 +235,21 @@
}
@Test
+ @EnableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS)
+ public void testInjectEvents_fromAccessibilityTool_providesToolPolicyFlag() {
+ EventStreamTransformation next = attachMockNext(mMotionEventInjector);
+ injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE,
+ /*fromAccessibilityTool=*/true);
+
+ mMessageCapturingHandler.sendOneMessage(); // Send a motion event
+ verify(next).onMotionEvent(
+ argThat(mIsLineStart), argThat(mIsLineStart),
+ eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY_TOOL));
+ }
+
+ @Test
public void testInjectEvents_gestureWithTooManyPoints_shouldNotCrash() throws Exception {
int tooManyPointsCount = 20;
TouchPoint[] startTouchPoints = new TouchPoint[tooManyPointsCount];
@@ -251,14 +274,28 @@
}
@Test
- public void testRegularEvent_afterGestureComplete_shouldPassToNext() {
+ @DisableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS)
+ public void testRegularEvent_afterGestureComplete_shouldPassToNext_withFlagInjectedFromA11y() {
EventStreamTransformation next = attachMockNext(mMotionEventInjector);
injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
mMessageCapturingHandler.sendAllMessages(); // Send all motion events
reset(next);
mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown),
- eq(FLAG_INJECTED_FROM_ACCESSIBILITY));
+ eq(WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
+ }
+
+ @Test
+ @EnableFlags(FLAG_PREVENT_A11Y_NONTOOL_FROM_INJECTING_INTO_SENSITIVE_VIEWS)
+ public void testRegularEvent_afterGestureComplete_shouldPassToNext_withNoPolicyFlagChanges() {
+ EventStreamTransformation next = attachMockNext(mMotionEventInjector);
+ injectEventsSync(mLineList, mServiceInterface, LINE_SEQUENCE);
+ mMessageCapturingHandler.sendAllMessages(); // Send all motion events
+ reset(next);
+ mMotionEventInjector.onMotionEvent(mClickDownEvent, mClickDownEvent, 0);
+ verify(next).onMotionEvent(argThat(mIsClickDown), argThat(mIsClickDown),
+ // The regular event passing through the filter should have no policy flag changes
+ eq(0));
}
@Test
@@ -275,7 +312,8 @@
mMessageCapturingHandler.sendOneMessage(); // Send a motion event
verify(next).onMotionEvent(
argThat(mIsLineStart), argThat(mIsLineStart),
- eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
+ eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
}
@Test
@@ -307,10 +345,12 @@
mMessageCapturingHandler.sendOneMessage(); // Send a motion event
verify(next).onMotionEvent(mCaptor1.capture(), mCaptor2.capture(),
- eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
+ eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
verify(next).onMotionEvent(
argThat(mIsLineStart), argThat(mIsLineStart),
- eq(FLAG_PASS_TO_USER | FLAG_INJECTED_FROM_ACCESSIBILITY));
+ eq(WindowManagerPolicyConstants.FLAG_PASS_TO_USER
+ | WindowManagerPolicyConstants.FLAG_INJECTED_FROM_ACCESSIBILITY));
}
@Test
@@ -731,8 +771,14 @@
private void injectEventsSync(List<GestureStep> gestureSteps,
IAccessibilityServiceClient serviceInterface, int sequence) {
+ injectEventsSync(gestureSteps, serviceInterface, sequence, false);
+ }
+
+ private void injectEventsSync(List<GestureStep> gestureSteps,
+ IAccessibilityServiceClient serviceInterface, int sequence,
+ boolean fromAccessibilityTool) {
mMotionEventInjector.injectEvents(gestureSteps, serviceInterface, sequence,
- Display.DEFAULT_DISPLAY);
+ Display.DEFAULT_DISPLAY, fromAccessibilityTool);
// Dispatch the message sent by the injector. Our simple handler doesn't guarantee stuff
// happens in order.
mMessageCapturingHandler.sendLastMessage();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index 3511ae1..cd6b36d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -885,66 +885,142 @@
}
@Test
- public void magnificationCallbacks_panMagnificationContinuous() throws RemoteException {
+ public void magnificationCallbacks_scaleMagnificationContinuous() throws RemoteException {
setMagnificationEnabled(MODE_FULLSCREEN);
- mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ float currentScale = 2.0f;
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, currentScale, false);
reset(mScreenMagnificationController);
- DisplayMetrics metrics = new DisplayMetrics();
- mDisplay.getMetrics(metrics);
- float expectedStep = 27 * metrics.density;
-
float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
- // Start moving right using keyboard callbacks.
+ // Start zooming in using keyboard callbacks.
+ mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
+
+ // The center is unchanged.
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isWithin(1.0f).of(newCenterX);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ // The scale is increased.
+ float newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ expect.that(currentScale).isLessThan(newScale);
+ currentScale = newScale;
+
+ // Wait for the initial delay to occur.
+ advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1);
+
+ // It should have scaled again after the handler was triggered.
+ newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ expect.that(currentScale).isLessThan(newScale);
+ currentScale = newScale;
+
+ for (int i = 0; i < 3; i++) {
+ // Wait for repeat delay to occur.
+ advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ // It should have scaled another time.
+ newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ expect.that(currentScale).isLessThan(newScale);
+ currentScale = newScale;
+ }
+
+ // Stop magnification scale.
+ mMagnificationController.onScaleMagnificationStop(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_IN);
+
+ // It should not scale again, even after the appropriate delay.
+ advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ expect.that(currentScale).isEqualTo(newScale);
+ }
+
+ @Test
+ public void magnificationCallbacks_panMagnificationContinuous_repeatKeysTimeout200()
+ throws RemoteException {
+ // Shorter than default.
+ testMagnificationContinuousPanningWithTimeout(200);
+ }
+
+ @Test
+ public void magnificationCallbacks_panMagnificationContinuous_repeatKeysTimeout1000()
+ throws RemoteException {
+ // Longer than default.
+ testMagnificationContinuousPanningWithTimeout(1000);
+ }
+
+ @Test
+ public void magnificationCallbacks_panMagnification_notContinuousWithRepeatKeysDisabled()
+ throws RemoteException {
+ mMagnificationController.setRepeatKeysEnabled(false);
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 4.0f, false);
+ reset(mScreenMagnificationController);
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ // Start moving down using keyboard callbacks.
mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
- MagnificationController.PAN_DIRECTION_RIGHT);
+ MagnificationController.PAN_DIRECTION_DOWN);
float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
- expect.that(currentCenterX).isLessThan(newCenterX);
- expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
- expect.that(currentCenterY).isEqualTo(newCenterY);
+ expect.that(currentCenterY).isLessThan(newCenterY);
+ expect.that(currentCenterX).isEqualTo(newCenterX);
currentCenterX = newCenterX;
currentCenterY = newCenterY;
- // Wait for the initial delay to occur.
- advanceTime(MagnificationController.INITIAL_KEYBOARD_REPEAT_INTERVAL_MS + 1);
+ for (int i = 0; i < 3; i++) {
+ // Wait for the initial delay to occur.
+ advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1);
- // It should have moved again after the handler was triggered.
- newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
- newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
- expect.that(currentCenterX).isLessThan(newCenterX);
- expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
- expect.that(currentCenterY).isEqualTo(newCenterY);
- currentCenterX = newCenterX;
- currentCenterY = newCenterY;
+ // It should not have moved again because repeat keys is disabled.
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isEqualTo(newCenterX);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+ }
- // Wait for repeat delay to occur.
- advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
-
- // It should have moved a third time.
- newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
- newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
- expect.that(currentCenterX).isLessThan(newCenterX);
- expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
- expect.that(currentCenterY).isEqualTo(newCenterY);
- currentCenterX = newCenterX;
- currentCenterY = newCenterY;
-
- // Stop magnification pan.
mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
- MagnificationController.PAN_DIRECTION_RIGHT);
+ MagnificationController.PAN_DIRECTION_DOWN);
+ }
- // It should not move again, even after the appropriate delay.
- advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+ @Test
+ public void magnificationCallbacks_scaleMagnification_notContinuousWithRepeatKeysDisabled()
+ throws RemoteException {
+ mMagnificationController.setRepeatKeysEnabled(false);
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ float currentScale = 8.0f;
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, currentScale, false);
+ reset(mScreenMagnificationController);
- newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
- newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
- expect.that(newCenterX).isEqualTo(currentCenterX);
- expect.that(newCenterY).isEqualTo(currentCenterY);
+ // Start scaling out using keyboard callbacks.
+ mMagnificationController.onScaleMagnificationStart(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
+
+ float newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ expect.that(currentScale).isGreaterThan(newScale);
+
+ currentScale = newScale;
+
+ for (int i = 0; i < 3; i++) {
+ // Wait for the initial delay to occur.
+ advanceTime(mMagnificationController.getInitialKeyboardRepeatIntervalMs() + 1);
+
+ // It should not have scaled again because repeat keys is disabled.
+ newScale = mScreenMagnificationController.getScale(TEST_DISPLAY);
+ expect.that(currentScale).isEqualTo(newScale);
+ }
+
+ mMagnificationController.onScaleMagnificationStop(TEST_DISPLAY,
+ MagnificationController.ZOOM_DIRECTION_OUT);
}
@Test
@@ -1736,6 +1812,75 @@
MagnificationController.PAN_DIRECTION_UP);
}
+ private void
+ testMagnificationContinuousPanningWithTimeout(int timeoutMs) throws RemoteException {
+ mMagnificationController.setRepeatKeysTimeoutMs(timeoutMs);
+ expect.that(timeoutMs).isEqualTo(
+ mMagnificationController.getInitialKeyboardRepeatIntervalMs());
+
+ setMagnificationEnabled(MODE_FULLSCREEN);
+ mMagnificationController.onPerformScaleAction(TEST_DISPLAY, 8.0f, false);
+ reset(mScreenMagnificationController);
+
+ DisplayMetrics metrics = new DisplayMetrics();
+ mDisplay.getMetrics(metrics);
+ float expectedStep = 27 * metrics.density;
+
+ float currentCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float currentCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+
+ // Start moving right using keyboard callbacks.
+ mMagnificationController.onPanMagnificationStart(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+
+ float newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ float newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ // Wait for the initial delay to occur.
+ advanceTime(timeoutMs + 1);
+
+ // It should have moved again after the handler was triggered.
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+
+ for (int i = 0; i < 3; i++) {
+ // Wait for repeat delay to occur.
+ advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ // It should have moved another time.
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(currentCenterX).isLessThan(newCenterX);
+ expect.that(newCenterX - currentCenterX).isWithin(0.01f).of(expectedStep);
+ expect.that(currentCenterY).isEqualTo(newCenterY);
+ currentCenterX = newCenterX;
+ currentCenterY = newCenterY;
+ }
+
+ // Stop magnification pan.
+ mMagnificationController.onPanMagnificationStop(TEST_DISPLAY,
+ MagnificationController.PAN_DIRECTION_RIGHT);
+
+ // It should not move again, even after the appropriate delay.
+ advanceTime(MagnificationController.KEYBOARD_REPEAT_INTERVAL_MS + 1);
+
+ newCenterX = mScreenMagnificationController.getCenterX(TEST_DISPLAY);
+ newCenterY = mScreenMagnificationController.getCenterY(TEST_DISPLAY);
+ expect.that(newCenterX).isEqualTo(currentCenterX);
+ expect.that(newCenterY).isEqualTo(currentCenterY);
+ }
+
private void advanceTime(long timeMs) {
mTestLooper.moveTimeForward(timeMs);
mTestLooper.dispatchAll();
diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
index 263ada8..148c968 100644
--- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java
@@ -69,7 +69,6 @@
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.platform.test.annotations.EnableFlags;
import android.service.quicksettings.TileService;
import android.testing.TestableContext;
@@ -80,7 +79,6 @@
import com.android.server.LocalServices;
import com.android.server.policy.GlobalActionsProvider;
import com.android.server.wm.ActivityTaskManagerInternal;
-import com.android.systemui.shared.Flags;
import libcore.junit.util.compat.CoreCompatChangeRule;
@@ -107,7 +105,6 @@
TEST_SERVICE);
private static final CharSequence APP_NAME = "AppName";
private static final CharSequence TILE_LABEL = "Tile label";
- private static final int SECONDARY_DISPLAY_ID = 2;
@Rule
public final TestableContext mContext =
@@ -752,29 +749,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
- public void testDisableForAllDisplays() throws Exception {
- int user1Id = 0;
- mockUidCheck();
- mockCurrentUserCheck(user1Id);
-
- mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID);
-
- int expectedFlags = DISABLE_MASK & DISABLE_BACK;
- String pkg = mContext.getPackageName();
-
- // before disabling
- assertEquals(DISABLE_NONE,
- mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
-
- // disable
- mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg);
-
- verify(mMockStatusBar).disable(0, expectedFlags, 0);
- verify(mMockStatusBar).disable(SECONDARY_DISPLAY_ID, expectedFlags, 0);
- }
-
- @Test
public void testSetHomeDisabled() throws Exception {
int expectedFlags = DISABLE_MASK & DISABLE_HOME;
String pkg = mContext.getPackageName();
@@ -877,29 +851,6 @@
}
@Test
- @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
- public void testDisable2ForAllDisplays() throws Exception {
- int user1Id = 0;
- mockUidCheck();
- mockCurrentUserCheck(user1Id);
-
- mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID);
-
- int expectedFlags = DISABLE2_MASK & DISABLE2_NOTIFICATION_SHADE;
- String pkg = mContext.getPackageName();
-
- // before disabling
- assertEquals(DISABLE_NONE,
- mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
-
- // disable
- mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg);
-
- verify(mMockStatusBar).disable(0, 0, expectedFlags);
- verify(mMockStatusBar).disable(SECONDARY_DISPLAY_ID, 0, expectedFlags);
- }
-
- @Test
public void testSetQuickSettingsDisabled2() throws Exception {
int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS;
String pkg = mContext.getPackageName();
@@ -1141,7 +1092,6 @@
// disable
mStatusBarManagerService.disableForUser(expectedUser1Flags, mMockStatusBar, pkg, user1Id);
mStatusBarManagerService.disableForUser(expectedUser2Flags, mMockStatusBar, pkg, user2Id);
-
// check that right flag is disabled
assertEquals(expectedUser1Flags,
mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index 839f276..19b90b6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -679,7 +679,7 @@
}
@Test
- @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
public void testDisallowAdjustmentType_readWriteXml_entries() throws Exception {
int userId = ActivityManager.getCurrentUser();
@@ -724,7 +724,7 @@
}
@Test
- @EnableFlags(android.service.notification.Flags.FLAG_NOTIFICATION_CLASSIFICATION)
+ @EnableFlags(Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
public void testDisallowAdjustmentKeyType_readWriteXml() throws Exception {
mAssistants.loadDefaultsFromConfig(true);
mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index caff913..1a95984 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -604,8 +604,7 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return FlagsParameterization.allCombinationsOf(
- FLAG_NOTIFICATION_CLASSIFICATION, FLAG_NM_BINDER_PERF_CACHE_CHANNELS);
+ return FlagsParameterization.allCombinationsOf();
}
public NotificationManagerServiceTest(FlagsParameterization flags) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
new file mode 100644
index 0000000..e0b700a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeHelperTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2025 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.server.wm;
+
+import static android.provider.Settings.Global.DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+import android.window.DesktopModeFlags;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.internal.R;
+import com.android.window.flags.Flags;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+
+/**
+ * Test class for {@link DesktopModeHelper}.
+ */
+@SmallTest
+@Presubmit
+@EnableFlags(Flags.FLAG_SHOW_DESKTOP_WINDOWING_DEV_OPTION)
+public class DesktopModeHelperTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ private Context mMockContext;
+ private Resources mMockResources;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+ mMockContext = mock(Context.class);
+ mMockResources = mock(Resources.class);
+
+ doReturn(mMockResources).when(mMockContext).getResources();
+ doReturn(false).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+ doReturn(false).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported));
+ doReturn(mContext.getContentResolver()).when(mMockContext).getContentResolver();
+ resetDesktopModeFlagsCache();
+ resetEnforceDeviceRestriction();
+ resetFlagOverride();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ resetDesktopModeFlagsCache();
+ resetEnforceDeviceRestriction();
+ resetFlagOverride();
+ }
+
+ @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+ @Test
+ public void canEnterDesktopMode_DWFlagDisabled_configsOff_returnsFalse() {
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+ }
+
+ @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+ @Test
+ public void canEnterDesktopMode_DWFlagDisabled_configsOn_disableDeviceCheck_returnsFalse()
+ throws Exception {
+ doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+ doReturn(true).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported));
+ disableEnforceDeviceRestriction();
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+ }
+
+ @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+ @Test
+ public void canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_returnsFalse() {
+ doReturn(true).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported));
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+ }
+
+ @DisableFlags({Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE,
+ Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION})
+ @Test
+ public void canEnterDesktopMode_DWFlagDisabled_configDevOptionOn_flagOverrideOn_returnsTrue()
+ throws Exception {
+ doReturn(true).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported));
+ setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON);
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ public void canEnterDesktopMode_DWFlagEnabled_configsOff_returnsFalse() {
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ public void canEnterDesktopMode_DWFlagEnabled_configDesktopModeOff_returnsFalse() {
+ doReturn(true).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported));
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isFalse();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ public void canEnterDesktopMode_DWFlagEnabled_configDesktopModeOn_returnsTrue() {
+ doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ public void canEnterDesktopMode_DWFlagEnabled_configsOff_disableDeviceRestrictions_returnsTrue()
+ throws Exception {
+ disableEnforceDeviceRestriction();
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE)
+ @Test
+ public void canEnterDesktopMode_DWFlagEnabled_configDevOptionOn_flagOverrideOn_returnsTrue() {
+ doReturn(true).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ );
+ setFlagOverride(DesktopModeFlags.ToggleOverride.OVERRIDE_ON);
+
+ assertThat(DesktopModeHelper.canEnterDesktopMode(mMockContext)).isTrue();
+ }
+
+ @Test
+ public void isDeviceEligibleForDesktopMode_configDEModeOn_returnsTrue() {
+ doReturn(true).when(mMockResources).getBoolean(eq(R.bool.config_isDesktopModeSupported));
+
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
+ }
+
+ @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @Test
+ public void isDeviceEligibleForDesktopMode_supportFlagOff_returnsFalse() {
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @Test
+ public void isDeviceEligibleForDesktopMode_supportFlagOn_returnsFalse() {
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isFalse();
+ }
+
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_MODE_THROUGH_DEV_OPTION)
+ @Test
+ public void isDeviceEligibleForDesktopMode_supportFlagOn_configDevOptModeOn_returnsTrue() {
+ doReturn(true).when(mMockResources).getBoolean(
+ eq(R.bool.config_isDesktopModeDevOptionSupported)
+ );
+
+ assertThat(DesktopModeHelper.isDeviceEligibleForDesktopMode(mMockContext)).isTrue();
+ }
+
+ private void resetEnforceDeviceRestriction() throws Exception {
+ setEnforceDeviceRestriction(true);
+ }
+
+ private void disableEnforceDeviceRestriction() throws Exception {
+ setEnforceDeviceRestriction(false);
+ }
+
+ private void setEnforceDeviceRestriction(boolean value) throws Exception {
+ Field deviceRestriction = DesktopModeHelper.class.getDeclaredField(
+ "ENFORCE_DEVICE_RESTRICTIONS");
+ deviceRestriction.setAccessible(true);
+ deviceRestriction.setBoolean(/* obj= */ null, /* z= */ value);
+ }
+
+ private void resetDesktopModeFlagsCache() throws Exception {
+ Field cachedToggleOverride = DesktopModeFlags.class.getDeclaredField(
+ "sCachedToggleOverride");
+ cachedToggleOverride.setAccessible(true);
+ cachedToggleOverride.set(/* obj= */ null, /* value= */ null);
+ }
+
+ private void resetFlagOverride() {
+ Settings.Global.putString(mContext.getContentResolver(),
+ DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, null);
+ }
+
+ private void setFlagOverride(DesktopModeFlags.ToggleOverride override) {
+ Settings.Global.putInt(mContext.getContentResolver(),
+ DEVELOPMENT_OVERRIDE_DESKTOP_MODE_FEATURES, override.getSetting());
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 7af4ede..c3aa289 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -4953,7 +4953,8 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatioAppliedForFixedOrientationCameraActivities() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -4965,7 +4966,8 @@
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// Create fixed portrait activity.
final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
@@ -4978,7 +4980,8 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatioForFixedOrientationCameraActivitiesPortraitWindow() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -4990,7 +4993,8 @@
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// Create fixed portrait activity.
final ActivityRecord fixedOrientationActivity = new ActivityBuilder(mAtm)
@@ -5003,7 +5007,8 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatioAppliedInsteadOfDefaultAspectRatio() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -5015,7 +5020,8 @@
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// App's target min aspect ratio - this should not be used, as camera controls aspect ratio.
final float targetMinAspectRatio = 4.0f;
@@ -5032,7 +5038,8 @@
}
@Test
- @EnableFlags(Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ @EnableFlags({Flags.FLAG_IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES,
+ Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING})
public void testCameraCompatAspectRatio_defaultAspectRatioAppliedWhenGreater() {
// Needed to create camera compat policy in DisplayContent.
allowDesktopMode();
@@ -5044,7 +5051,8 @@
setupCameraCompatAspectRatio(cameraCompatAspectRatio, display);
// Create task on test display.
- final Task task = new TaskBuilder(mSupervisor).setDisplay(display).build();
+ final Task task = new TaskBuilder(mSupervisor).setDisplay(display)
+ .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
// App's target min aspect ratio bigger than camera compat aspect ratio - use that instead.
final float targetMinAspectRatio = 6.0f;
diff --git a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
index a0e0477..1fb18a6 100644
--- a/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
+++ b/tests/CompanionDeviceMultiDeviceTests/host/Android.bp
@@ -39,13 +39,4 @@
device_common_data: [
":cdm_snippet_legacy",
],
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: true,
- },
- },
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 03d6ce8..18f44dd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -51,8 +51,8 @@
instrumentation.targetContext.contentResolver,
Settings.System.HIDE_ROTATION_LOCK_TOGGLE_FOR_ACCESSIBILITY,
0,
- UserHandle.USER_CURRENT
- );
+ UserHandle.USER_CURRENT_OR_SELF
+ )
}
private val logTag = this::class.java.simpleName
diff --git a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
index 1c2a053..c2f9adf 100644
--- a/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
+++ b/tests/Input/src/android/hardware/input/StickyModifierStateListenerTest.kt
@@ -50,10 +50,7 @@
*/
@Presubmit
@RunWith(MockitoJUnitRunner::class)
-@EnableFlags(
- com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
- com.android.input.flags.Flags.FLAG_ENABLE_INPUT_FILTER_RUST_IMPL,
-)
+@EnableFlags(com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG)
class StickyModifierStateListenerTest {
@get:Rule
diff --git a/tests/utils/testutils/java/android/os/test/TestLooper.java b/tests/utils/testutils/java/android/os/test/TestLooper.java
index 83d22d9..61fa7b5 100644
--- a/tests/utils/testutils/java/android/os/test/TestLooper.java
+++ b/tests/utils/testutils/java/android/os/test/TestLooper.java
@@ -18,18 +18,24 @@
import static org.junit.Assert.assertTrue;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.SystemClock;
+import android.os.TestLooperManager;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.ArrayDeque;
+import java.util.Queue;
import java.util.concurrent.Executor;
/**
@@ -44,7 +50,9 @@
* The Robolectric class also allows advancing time.
*/
public class TestLooper {
- protected final Looper mLooper;
+ private final Looper mLooper;
+ private final TestLooperManager mTestLooperManager;
+ private final Clock mClock;
private static final Constructor<Looper> LOOPER_CONSTRUCTOR;
private static final Field THREAD_LOCAL_LOOPER_FIELD;
@@ -54,24 +62,37 @@
private static final Method MESSAGE_MARK_IN_USE_METHOD;
private static final String TAG = "TestLooper";
- private final Clock mClock;
-
private AutoDispatchThread mAutoDispatchThread;
+ /**
+ * Baklava introduces new {@link TestLooperManager} APIs that we can use instead of reflection.
+ */
+ private static boolean isAtLeastBaklava() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA;
+ }
+
static {
try {
LOOPER_CONSTRUCTOR = Looper.class.getDeclaredConstructor(Boolean.TYPE);
LOOPER_CONSTRUCTOR.setAccessible(true);
THREAD_LOCAL_LOOPER_FIELD = Looper.class.getDeclaredField("sThreadLocal");
THREAD_LOCAL_LOOPER_FIELD.setAccessible(true);
- MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
- MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
- MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
- MESSAGE_NEXT_FIELD.setAccessible(true);
- MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
- MESSAGE_WHEN_FIELD.setAccessible(true);
- MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
- MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
+
+ if (isAtLeastBaklava()) {
+ MESSAGE_QUEUE_MESSAGES_FIELD = null;
+ MESSAGE_NEXT_FIELD = null;
+ MESSAGE_WHEN_FIELD = null;
+ MESSAGE_MARK_IN_USE_METHOD = null;
+ } else {
+ MESSAGE_QUEUE_MESSAGES_FIELD = MessageQueue.class.getDeclaredField("mMessages");
+ MESSAGE_QUEUE_MESSAGES_FIELD.setAccessible(true);
+ MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
+ MESSAGE_NEXT_FIELD.setAccessible(true);
+ MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
+ MESSAGE_WHEN_FIELD.setAccessible(true);
+ MESSAGE_MARK_IN_USE_METHOD = Message.class.getDeclaredMethod("markInUse");
+ MESSAGE_MARK_IN_USE_METHOD.setAccessible(true);
+ }
} catch (NoSuchFieldException | NoSuchMethodException e) {
throw new RuntimeException("Failed to initialize TestLooper", e);
}
@@ -106,6 +127,13 @@
throw new RuntimeException("Reflection error constructing or accessing looper", e);
}
+ if (isAtLeastBaklava()) {
+ mTestLooperManager =
+ InstrumentationRegistry.getInstrumentation().acquireLooperManager(mLooper);
+ } else {
+ mTestLooperManager = null;
+ }
+
mClock = clock;
}
@@ -117,19 +145,72 @@
return new HandlerExecutor(new Handler(getLooper()));
}
- private Message getMessageLinkedList() {
+ private Message getMessageLinkedListLegacy() {
try {
MessageQueue queue = mLooper.getQueue();
return (Message) MESSAGE_QUEUE_MESSAGES_FIELD.get(queue);
} catch (IllegalAccessException e) {
throw new RuntimeException("Access failed in TestLooper: get - MessageQueue.mMessages",
- e);
+ e);
}
}
public void moveTimeForward(long milliSeconds) {
+ if (isAtLeastBaklava()) {
+ moveTimeForwardBaklava(milliSeconds);
+ } else {
+ moveTimeForwardLegacy(milliSeconds);
+ }
+ }
+
+ private void moveTimeForwardBaklava(long milliSeconds) {
+ // Drain all Messages from the queue.
+ Queue<Message> messages = new ArrayDeque<>();
+ while (true) {
+ Message message = mTestLooperManager.poll();
+ if (message == null) {
+ break;
+ }
+
+ // Adjust the Message's delivery time.
+ long newWhen = message.when - milliSeconds;
+ if (newWhen < 0) {
+ newWhen = 0;
+ }
+ message.when = newWhen;
+ messages.add(message);
+ }
+
+ // Repost all Messages back to the queuewith a new time.
+ while (true) {
+ Message message = messages.poll();
+ if (message == null) {
+ break;
+ }
+
+ Runnable callback = message.getCallback();
+ Handler handler = message.getTarget();
+ long when = message.getWhen();
+
+ // The Message cannot be re-enqueued because it is marked in use.
+ // Make a copy of the Message and recycle the original.
+ // This resets {@link Message#isInUse()} but retains all other content.
+ {
+ Message newMessage = Message.obtain();
+ newMessage.copyFrom(message);
+ newMessage.setCallback(callback);
+ mTestLooperManager.recycle(message);
+ message = newMessage;
+ }
+
+ // Send the Message back to its Handler to be re-enqueued.
+ handler.sendMessageAtTime(message, when);
+ }
+ }
+
+ private void moveTimeForwardLegacy(long milliSeconds) {
try {
- Message msg = getMessageLinkedList();
+ Message msg = getMessageLinkedListLegacy();
while (msg != null) {
long updatedWhen = msg.getWhen() - milliSeconds;
if (updatedWhen < 0) {
@@ -147,12 +228,12 @@
return mClock.uptimeMillis();
}
- private Message messageQueueNext() {
+ private Message messageQueueNextLegacy() {
try {
long now = currentTime();
Message prevMsg = null;
- Message msg = getMessageLinkedList();
+ Message msg = getMessageLinkedListLegacy();
if (msg != null && msg.getTarget() == null) {
// Stalled by a barrier. Find the next asynchronous message in
// the queue.
@@ -185,18 +266,46 @@
/**
* @return true if there are pending messages in the message queue
*/
- public synchronized boolean isIdle() {
- Message messageList = getMessageLinkedList();
+ public boolean isIdle() {
+ if (isAtLeastBaklava()) {
+ return isIdleBaklava();
+ } else {
+ return isIdleLegacy();
+ }
+ }
+ private boolean isIdleBaklava() {
+ Long when = mTestLooperManager.peekWhen();
+ return when != null && currentTime() >= when;
+ }
+
+ private synchronized boolean isIdleLegacy() {
+ Message messageList = getMessageLinkedListLegacy();
return messageList != null && currentTime() >= messageList.getWhen();
}
/**
* @return the next message in the Looper's message queue or null if there is none
*/
- public synchronized Message nextMessage() {
+ public Message nextMessage() {
+ if (isAtLeastBaklava()) {
+ return nextMessageBaklava();
+ } else {
+ return nextMessageLegacy();
+ }
+ }
+
+ private Message nextMessageBaklava() {
if (isIdle()) {
- return messageQueueNext();
+ return mTestLooperManager.poll();
+ } else {
+ return null;
+ }
+ }
+
+ private synchronized Message nextMessageLegacy() {
+ if (isIdle()) {
+ return messageQueueNextLegacy();
} else {
return null;
}
@@ -206,9 +315,26 @@
* Dispatch the next message in the queue
* Asserts that there is a message in the queue
*/
- public synchronized void dispatchNext() {
+ public void dispatchNext() {
+ if (isAtLeastBaklava()) {
+ dispatchNextBaklava();
+ } else {
+ dispatchNextLegacy();
+ }
+ }
+
+ private void dispatchNextBaklava() {
assertTrue(isIdle());
- Message msg = messageQueueNext();
+ Message msg = mTestLooperManager.poll();
+ if (msg == null) {
+ return;
+ }
+ msg.getTarget().dispatchMessage(msg);
+ }
+
+ private synchronized void dispatchNextLegacy() {
+ assertTrue(isIdle());
+ Message msg = messageQueueNextLegacy();
if (msg == null) {
return;
}