Merge "Guard against crashes in effects services" into main
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
index 459c286..515ddc8 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/BenchmarkRunner.java
@@ -19,16 +19,13 @@
import android.os.Bundle;
import android.os.SystemClock;
import android.perftests.utils.ShellHelper;
-import android.util.Log;
import java.util.ArrayList;
// Based on //platform/frameworks/base/apct-tests/perftests/utils/BenchmarkState.java
public class BenchmarkRunner {
- private static final String TAG = BenchmarkRunner.class.getSimpleName();
+
private static final long COOL_OFF_PERIOD_MS = 1000;
- private static final int CPU_IDLE_TIMEOUT_MS = 60 * 1000;
- private static final int CPU_IDLE_THRESHOLD_PERCENTAGE = 90;
private static final int NUM_ITERATIONS = 4;
@@ -83,7 +80,7 @@
private void prepareForNextRun() {
SystemClock.sleep(COOL_OFF_PERIOD_MS);
- waitCoolDownPeriod();
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
mStartTimeNs = System.nanoTime();
mPausedDurationNs = 0;
}
@@ -105,7 +102,7 @@
* to avoid unnecessary waiting.
*/
public void resumeTiming() {
- waitCoolDownPeriod();
+ ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
resumeTimer();
}
@@ -165,56 +162,4 @@
}
return null;
}
-
- /** Waits for the broadcast queue and the CPU cores to be idle. */
- public void waitCoolDownPeriod() {
- waitForBroadcastIdle();
- waitForCpuIdle();
- }
-
- private void waitForBroadcastIdle() {
- Log.d(TAG, "starting to waitForBroadcastIdle");
- final long startedAt = System.currentTimeMillis();
- ShellHelper.runShellCommand("am wait-for-broadcast-idle --flush-broadcast-loopers");
- final long elapsed = System.currentTimeMillis() - startedAt;
- Log.d(TAG, "waitForBroadcastIdle is complete in " + elapsed + " ms");
- }
- private void waitForCpuIdle() {
- Log.d(TAG, "starting to waitForCpuIdle");
- final long startedAt = System.currentTimeMillis();
- while (true) {
- final int idleCpuPercentage = getIdleCpuPercentage();
- final long elapsed = System.currentTimeMillis() - startedAt;
- Log.d(TAG, "waitForCpuIdle " + idleCpuPercentage + "% (" + elapsed + "ms elapsed)");
- if (idleCpuPercentage >= CPU_IDLE_THRESHOLD_PERCENTAGE) {
- Log.d(TAG, "waitForCpuIdle is complete in " + elapsed + " ms");
- return;
- }
- if (elapsed >= CPU_IDLE_TIMEOUT_MS) {
- Log.e(TAG, "Ending waitForCpuIdle because it didn't finish in "
- + CPU_IDLE_TIMEOUT_MS + " ms");
- return;
- }
- SystemClock.sleep(1000);
- }
- }
-
- private int getIdleCpuPercentage() {
- String output = ShellHelper.runShellCommand("top -m 1 -n 1");
- String[] tokens = output.split("\\s+");
- float totalCpu = -1;
- float idleCpu = -1;
- for (String token : tokens) {
- if (token.contains("%cpu")) {
- totalCpu = Float.parseFloat(token.split("%")[0]);
- } else if (token.contains("%idle")) {
- idleCpu = Float.parseFloat(token.split("%")[0]);
- }
- }
- if (totalCpu < 0 || idleCpu < 0) {
- Log.e(TAG, "Could not get idle cpu percentage, output=" + output);
- return -1;
- }
- return (int) (100 * idleCpu / totalCpu);
- }
}
\ No newline at end of file
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 98ab0c2..762e2af 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -188,6 +188,21 @@
}
}
+ /** Tests creating a new user, with wait times between iterations. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void createUser_realistic() throws RemoteException {
+ while (mRunner.keepRunning()) {
+ Log.i(TAG, "Starting timer");
+ final int userId = createUserNoFlags();
+
+ mRunner.pauseTiming();
+ Log.i(TAG, "Stopping timer");
+ removeUser(userId);
+ waitCoolDownPeriod();
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests creating and starting a new user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void createAndStartUser() throws RemoteException {
@@ -224,6 +239,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -238,6 +254,7 @@
mRunner.pauseTiming();
final int userId = createUserNoFlags();
+ waitForBroadcastIdle();
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -292,6 +309,9 @@
preStartUser(userId, numberOfIterationsToSkip);
+ waitForBroadcastIdle();
+ waitCoolDownPeriod();
+
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -333,6 +353,9 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitForBroadcastIdle();
+ waitCoolDownPeriod();
+
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -397,6 +420,7 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -430,6 +454,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -441,7 +466,6 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -455,6 +479,27 @@
}
}
+ /** Tests switching to an uninitialized user with wait times between iterations. */
+ @Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
+ public void switchUser_realistic() throws Exception {
+ while (mRunner.keepRunning()) {
+ mRunner.pauseTiming();
+ final int startUser = ActivityManager.getCurrentUser();
+ final int userId = createUserNoFlags();
+ waitCoolDownPeriod();
+ Log.d(TAG, "Starting timer");
+ mRunner.resumeTiming();
+
+ switchUser(userId);
+
+ mRunner.pauseTiming();
+ Log.d(TAG, "Stopping timer");
+ switchUserNoCheck(startUser);
+ removeUser(userId);
+ mRunner.resumeTimingForNextIteration();
+ }
+ }
+
/** Tests switching to a previously-started, but no-longer-running, user. */
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void switchUser_stopped() throws RemoteException {
@@ -462,7 +507,6 @@
mRunner.pauseTiming();
final int startUser = mAm.getCurrentUser();
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ true);
-
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -492,6 +536,7 @@
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -517,6 +562,7 @@
/* useStaticWallpaper */true);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -560,6 +606,7 @@
final int testUser = initializeNewUserAndSwitchBack(/* stopNewUser */ false);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -567,6 +614,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
+ waitForBroadcastIdle();
switchUserNoCheck(startUser);
mRunner.resumeTimingForNextIteration();
}
@@ -583,6 +631,7 @@
/* useStaticWallpaper */ true);
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -590,6 +639,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
+ waitForBroadcastIdle();
switchUserNoCheck(startUser);
mRunner.resumeTimingForNextIteration();
}
@@ -625,11 +675,13 @@
@Test(timeout = TIMEOUT_MAX_TEST_TIME_MS)
public void stopUser_realistic() throws RemoteException {
final int userId = createUserNoFlags();
+ waitCoolDownPeriod();
while (mRunner.keepRunning()) {
mRunner.pauseTiming();
runThenWaitForBroadcasts(userId, ()-> {
mIam.startUserInBackground(userId);
}, Intent.ACTION_USER_STARTED, Intent.ACTION_MEDIA_MOUNTED);
+ waitCoolDownPeriod();
Log.d(TAG, "Starting timer");
mRunner.resumeTiming();
@@ -651,7 +703,7 @@
final int startUser = mAm.getCurrentUser();
final int userId = createUserNoFlags();
- mRunner.waitCoolDownPeriod();
+ waitForBroadcastIdle();
mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
mRunner.resumeTiming();
Log.i(TAG, "Starting timer");
@@ -674,7 +726,7 @@
final int startUser = ActivityManager.getCurrentUser();
final int userId = createUserNoFlags();
- mRunner.waitCoolDownPeriod();
+ waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilBootCompleted(userId, () -> {
mRunner.resumeTiming();
Log.d(TAG, "Starting timer");
@@ -700,7 +752,7 @@
switchUser(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- mRunner.waitCoolDownPeriod();
+ waitForBroadcastIdle();
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
@@ -729,7 +781,7 @@
switchUser(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
- mRunner.waitCoolDownPeriod();
+ waitCoolDownPeriod();
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(startUser, () -> {
runThenWaitForBroadcasts(userId, () -> {
mRunner.resumeTiming();
@@ -775,6 +827,7 @@
Log.d(TAG, "Stopping timer");
attestTrue("Failed creating profile " + userId, mUm.isManagedProfile(userId));
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -815,6 +868,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -859,6 +913,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
removeUser(userId);
@@ -910,6 +965,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -974,6 +1030,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1014,6 +1071,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1066,6 +1124,7 @@
mRunner.pauseTiming();
Log.d(TAG, "Stopping timer");
removeUser(userId);
+ waitCoolDownPeriod();
mRunner.resumeTimingForNextIteration();
}
}
@@ -1105,6 +1164,7 @@
runThenWaitForBroadcasts(userId, () -> {
startUserInBackgroundAndWaitForUnlock(userId);
}, Intent.ACTION_MEDIA_MOUNTED);
+ waitCoolDownPeriod();
mRunner.resumeTiming();
Log.d(TAG, "Starting timer");
@@ -1220,7 +1280,6 @@
* If lack of success should fail the test, use {@link #switchUser(int)} instead.
*/
private boolean switchUserNoCheck(int userId) throws RemoteException {
- mRunner.waitCoolDownPeriod();
final boolean[] success = {true};
mUserSwitchWaiter.runThenWaitUntilSwitchCompleted(userId, () -> {
mAm.switchUser(userId);
@@ -1237,7 +1296,7 @@
*/
private void stopUserAfterWaitingForBroadcastIdle(int userId)
throws RemoteException {
- mRunner.waitCoolDownPeriod();
+ waitForBroadcastIdle();
stopUser(userId);
}
@@ -1379,8 +1438,6 @@
*/
private void runThenWaitForBroadcasts(int userId, FunctionalUtils.ThrowingRunnable runnable,
String... actions) {
- mRunner.waitCoolDownPeriod();
-
final String unreceivedAction =
mBroadcastWaiter.runThenWaitForBroadcasts(userId, runnable, actions);
@@ -1481,4 +1538,28 @@
assertEquals("", ShellHelper.runShellCommand("setprop " + name + " " + value));
return TextUtils.firstNotEmpty(oldValue, "invalid");
}
+
+ private void waitForBroadcastIdle() {
+ try {
+ ShellHelper.runShellCommandWithTimeout(
+ "am wait-for-broadcast-idle --flush-broadcast-loopers", TIMEOUT_IN_SECOND);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "Ending waitForBroadcastIdle because it is taking too long", e);
+ }
+ }
+
+ private void sleep(long ms) {
+ try {
+ Thread.sleep(ms);
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+
+ private void waitCoolDownPeriod() {
+ // Heuristic value based on local tests. Stability increased compared to no waiting.
+ final int tenSeconds = 1000 * 10;
+ waitForBroadcastIdle();
+ sleep(tenSeconds);
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 44444b5..67752f2 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -62,6 +62,7 @@
import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
+import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
@@ -17626,6 +17627,17 @@
return onboardingBugreportV2Enabled();
}
+ // TODO(b/308755220): Remove once the build is finalised.
+ /**
+ * Returns true if the flag for consentless bugreports is enabled.
+ *
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public boolean isOnboardingConsentlessBugreportFlagEnabled() {
+ return onboardingConsentlessBugreports();
+ }
+
/**
* Returns the subscription ids of all subscriptions which were downloaded by the calling
* admin.
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index e2a131c..5668c54 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -140,6 +140,16 @@
}
}
+flag {
+ name: "fix_avatar_concurrent_file_write"
+ namespace: "multiuser"
+ description: "Fix potential unexpected behavior due to concurrent file writing"
+ bug: "339351031"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
# This flag guards the private space feature and all its implementations excluding the APIs. APIs are guarded by android.os.Flags.allow_private_profile.
flag {
name: "enable_private_space_features"
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 9eabc8d..53771e3 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -963,11 +963,10 @@
* originate from the system, just that we were unable to verify it. This can
* happen for a number of reasons during normal operation.
*
- * @param event The {@link android.view.InputEvent} to check
+ * @param event The {@link android.view.InputEvent} to check.
*
* @return {@link android.view.VerifiedInputEvent}, which is a subset of the provided
- * {@link android.view.InputEvent}
- * {@code null} if the event could not be verified.
+ * {@link android.view.InputEvent}, or {@code null} if the event could not be verified.
*/
@Nullable
public VerifiedInputEvent verifyInputEvent(@NonNull InputEvent event) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index ba1915c..15b0c13 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -2263,6 +2263,7 @@
this(modeId, width, height, refreshRate, vsyncRate, false, alternativeRefreshRates,
supportedHdrTypes);
}
+
/**
* @hide
*/
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index da86e2d..8b9d876 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -213,7 +213,7 @@
/**
* The supported modes that will be exposed externally.
- * Might have different set of modes that supportedModes for VRR displays
+ * Might have different set of modes than supportedModes for VRR displays
*/
public Display.Mode[] appsSupportedModes = Display.Mode.EMPTY_ARRAY;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 6db40bf..79a9f2d 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -682,7 +682,8 @@
* <li>For a touch screen or touch pad, reports the approximate size of the contact area in
* relation to the maximum detectable size for the device. The value is normalized
* to a range from 0 (smallest detectable size) to 1 (largest detectable size),
- * although it is not a linear scale. This value is of limited use.
+ * although it is not a linear scale. The value of size can be used to
+ * determine fat touch events.
* To obtain calibrated size information, use
* {@link #AXIS_TOUCH_MAJOR} or {@link #AXIS_TOOL_MAJOR}.
* </ul>
@@ -2795,13 +2796,8 @@
}
/**
- * Returns the current pressure of this event for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * The pressure generally
- * ranges from 0 (no pressure at all) to 1 (normal pressure), however
- * values higher than 1 may be generated depending on the calibration of
- * the input device.
+ * Returns the value of {@link #AXIS_PRESSURE} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2812,14 +2808,8 @@
}
/**
- * Returns a scaled value of the approximate size for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * This represents some approximation of the area of the screen being
- * pressed; the actual value in pixels corresponding to the
- * touch is normalized with the device specific range of values
- * and scaled to a value between 0 and 1. The value of size can be used to
- * determine fat touch events.
+ * Returns the value of {@link #AXIS_SIZE} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2830,10 +2820,8 @@
}
/**
- * Returns the length of the major axis of an ellipse that describes the touch
- * area at the point of contact for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
+ * Returns the value of {@link #AXIS_TOUCH_MAJOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2844,10 +2832,8 @@
}
/**
- * Returns the length of the minor axis of an ellipse that describes the touch
- * area at the point of contact for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
+ * Returns the value of {@link #AXIS_TOUCH_MINOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2858,12 +2844,8 @@
}
/**
- * Returns the length of the major axis of an ellipse that describes the size of
- * the approaching tool for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * The tool area represents the estimated size of the finger or pen that is
- * touching the device independent of its actual touch area at the point of contact.
+ * Returns the value of {@link #AXIS_TOOL_MAJOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2874,12 +2856,8 @@
}
/**
- * Returns the length of the minor axis of an ellipse that describes the size of
- * the approaching tool for the given pointer
- * <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * The tool area represents the estimated size of the finger or pen that is
- * touching the device independent of its actual touch area at the point of contact.
+ * Returns the value of {@link #AXIS_TOOL_MINOR} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
@@ -2890,15 +2868,8 @@
}
/**
- * Returns the orientation of the touch area and tool area in radians clockwise from vertical
- * for the given pointer <em>index</em> (use {@link #getPointerId(int)} to find the pointer
- * identifier for this index).
- * An angle of 0 radians indicates that the major axis of contact is oriented
- * upwards, is perfectly circular or is of unknown orientation. A positive angle
- * indicates that the major axis of contact is oriented to the right. A negative angle
- * indicates that the major axis of contact is oriented to the left.
- * The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians
- * (finger pointing fully right).
+ * Returns the value of {@link #AXIS_ORIENTATION} for the given pointer <em>index</em>.
+ *
* @param pointerIndex Raw index of pointer to retrieve. Value may be from 0
* (the first pointer that is down) to {@link #getPointerCount()}-1.
*
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 075ee1b..5e3f09a 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1984,9 +1984,25 @@
public @interface ContentSensitivity {}
/**
- * Automatically determine whether a view displays sensitive content. For example, available
- * autofill hints (or some other signal) can be used to determine if this view
- * displays sensitive content.
+ * Content sensitivity is determined by the framework. The framework uses a heuristic to
+ * determine if this view displays sensitive content.
+ * Autofill hints i.e. {@link #getAutofillHints()} are used in the heuristic
+ * to determine if this view should be considered as a sensitive view.
+ * <p>
+ * {@link #AUTOFILL_HINT_USERNAME},
+ * {@link #AUTOFILL_HINT_PASSWORD},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_NUMBER},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_SECURITY_CODE},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DAY},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_MONTH},
+ * {@link #AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_YEAR}
+ * are considered sensitive hints by the framework, and the list may include more hints
+ * in the future.
+ *
+ * <p> The window hosting a sensitive view will be marked as secure during an active media
+ * projection session. This would be equivalent to applying
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
*
* @see #getContentSensitivity()
*/
@@ -1996,6 +2012,10 @@
/**
* The view displays sensitive content.
*
+ * <p> The window hosting a sensitive view will be marked as secure during an active media
+ * projection session. This would be equivalent to applying
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
+ *
* @see #getContentSensitivity()
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
@@ -10548,9 +10568,13 @@
/**
* Sets content sensitivity mode to determine whether this view displays sensitive content
- * (e.g. username, password etc.). The system may improve user privacy i.e. hide content
+ * (e.g. username, password etc.). The system will improve user privacy i.e. hide content
* drawn by a sensitive view from screen sharing and recording.
*
+ * <p> The window hosting a sensitive view will be marked as secure during an active media
+ * projection session. This would be equivalent to applying
+ * {@link android.view.WindowManager.LayoutParams#FLAG_SECURE} to the window.
+ *
* @param mode {@link #CONTENT_SENSITIVITY_AUTO}, {@link #CONTENT_SENSITIVITY_NOT_SENSITIVE}
* or {@link #CONTENT_SENSITIVITY_SENSITIVE}
*/
@@ -10574,8 +10598,7 @@
* {@link #setContentSensitivity(int)}.
*/
@FlaggedApi(FLAG_SENSITIVE_CONTENT_APP_PROTECTION_API)
- public @ContentSensitivity
- final int getContentSensitivity() {
+ public @ContentSensitivity final int getContentSensitivity() {
return (mPrivateFlags4 & PFLAG4_CONTENT_SENSITIVITY_MASK)
>> PFLAG4_CONTENT_SENSITIVITY_SHIFT;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0f54940b..42bf420 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2801,6 +2801,10 @@
* it from appearing in screenshots or from being viewed on non-secure
* displays.
*
+ * <p>See {@link android.view.View#setContentSensitivity(int)}, a window hosting
+ * a sensitive view will be marked as secure during media projection, preventing
+ * it from being viewed on non-secure displays and during screen share.
+ *
* <p>See {@link android.view.Display#FLAG_SECURE} for more details about
* secure surfaces and secure displays.
*/
diff --git a/core/java/com/android/internal/compat/ChangeReporter.java b/core/java/com/android/internal/compat/ChangeReporter.java
index ded142c..d8f1309 100644
--- a/core/java/com/android/internal/compat/ChangeReporter.java
+++ b/core/java/com/android/internal/compat/ChangeReporter.java
@@ -98,7 +98,7 @@
public void reportChange(int uid, long changeId, int state, boolean isLoggableBySdk) {
boolean isAlreadyReported =
checkAndSetIsAlreadyReported(uid, new ChangeReport(changeId, state));
- if (!isAlreadyReported) {
+ if (shouldWriteToStatsLog(isAlreadyReported)) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPATIBILITY_CHANGE_REPORTED, uid,
changeId, state, mSource);
}
@@ -136,14 +136,16 @@
/**
* Returns whether the next report should be logged to FrameworkStatsLog.
*
- * @param uid affected by the change
- * @param changeId the reported change id
- * @param state of the reported change - enabled/disabled/only logged
+ * @param isAlreadyReported is the change already reported
* @return true if the report should be logged
*/
@VisibleForTesting
- boolean shouldWriteToStatsLog(int uid, long changeId, int state) {
- return !isAlreadyReported(uid, new ChangeReport(changeId, state));
+ boolean shouldWriteToStatsLog(boolean isAlreadyReported) {
+ // We don't log for system server
+ if (mSource == SOURCE_SYSTEM_SERVER) return false;
+
+ // Don't log if already reported
+ return !isAlreadyReported;
}
/**
@@ -160,8 +162,6 @@
boolean isAlreadyReported, int state, boolean isLoggableBySdk) {
// If log all bit is on, always return true.
if (mDebugLogAll) return true;
- // If the change has already been reported, do not write.
- if (isAlreadyReported) return false;
// If the flag is turned off or the TAG's logging is forced to debug level with
// `adb setprop log.tag.CompatChangeReporter=DEBUG`, write to debug since the above checks
@@ -224,6 +224,19 @@
return mReportedChanges.getOrDefault(uid, EMPTY_SET).contains(report);
}
+ /**
+ * Returns whether the next report should be logged.
+ *
+ * @param uid affected by the change
+ * @param changeId the reported change id
+ * @param state of the reported change - enabled/disabled/only logged
+ * @return true if the report should be logged
+ */
+ @VisibleForTesting
+ boolean isAlreadyReported(int uid, long changeId, int state) {
+ return isAlreadyReported(uid, new ChangeReport(changeId, state));
+ }
+
private void markAsReported(int uid, ChangeReport report) {
mReportedChanges.computeIfAbsent(uid, NEW_CHANGE_REPORT_SET).add(report);
}
diff --git a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
index 12a42f9..c32fd45 100644
--- a/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
+++ b/core/tests/PlatformCompatFramework/src/com/android/internal/compat/ChangeReporterTest.java
@@ -31,21 +31,21 @@
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Test
- public void testStatsLogOnce() {
+ public void testIsAlreadyReportedOnce() {
ChangeReporter reporter = new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
int myUid = 1022, otherUid = 1023;
long myChangeId = 500L, otherChangeId = 600L;
int myState = ChangeReporter.STATE_ENABLED, otherState = ChangeReporter.STATE_DISABLED;
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
// Same report will not be logged again.
- assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.isAlreadyReported(myUid, myChangeId, myState));
// Other reports will be logged.
- assertTrue(reporter.shouldWriteToStatsLog(otherUid, myChangeId, myState));
- assertTrue(reporter.shouldWriteToStatsLog(myUid, otherChangeId, myState));
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, otherState));
+ assertFalse(reporter.isAlreadyReported(otherUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, otherChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, otherState));
}
@Test
@@ -55,15 +55,15 @@
long myChangeId = 500L;
int myState = ChangeReporter.STATE_ENABLED;
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
reporter.reportChange(myUid, myChangeId, myState);
// Same report will not be logged again.
- assertFalse(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertTrue(reporter.isAlreadyReported(myUid, myChangeId, myState));
reporter.resetReportedChanges(myUid);
// Same report will be logged again after reset.
- assertTrue(reporter.shouldWriteToStatsLog(myUid, myChangeId, myState));
+ assertFalse(reporter.isAlreadyReported(myUid, myChangeId, myState));
}
@Test
@@ -196,4 +196,21 @@
// off.
assertTrue(reporter.shouldWriteToDebug(myUid, myChangeId, myDisabledState, false));
}
+
+ @Test
+ public void testDontLogSystemServer() {
+ ChangeReporter systemServerReporter =
+ new ChangeReporter(ChangeReporter.SOURCE_SYSTEM_SERVER);
+
+ // We never log from system server, even if already reported.
+ assertFalse(systemServerReporter.shouldWriteToStatsLog(false));
+ assertFalse(systemServerReporter.shouldWriteToStatsLog(true));
+
+ ChangeReporter unknownSourceReporter =
+ new ChangeReporter(ChangeReporter.SOURCE_UNKNOWN_SOURCE);
+
+ // Otherwise we log if it's not already been reported.
+ assertTrue(unknownSourceReporter.shouldWriteToStatsLog(false));
+ assertFalse(unknownSourceReporter.shouldWriteToStatsLog(true));
+ }
}
diff --git a/core/tests/coretests/src/android/util/BinaryXmlTest.java b/core/tests/coretests/src/android/util/BinaryXmlTest.java
index 025e831..da29828 100644
--- a/core/tests/coretests/src/android/util/BinaryXmlTest.java
+++ b/core/tests/coretests/src/android/util/BinaryXmlTest.java
@@ -24,6 +24,8 @@
import static android.util.XmlTest.doVerifyWrite;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.fail;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
import android.os.PersistableBundle;
@@ -41,12 +43,15 @@
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
@RunWith(AndroidJUnit4.class)
public class BinaryXmlTest {
+ private static final int MAX_UNSIGNED_SHORT = 65_535;
+
/**
* Verify that we can write and read large numbers of interned
* {@link String} values.
@@ -170,4 +175,49 @@
}
}
}
+
+ @Test
+ public void testAttributeBytes_BinaryDataOverflow() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT + 1];
+ assertThrows(IOException.class,
+ () -> out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex",
+ testBytes));
+
+ assertThrows(IOException.class,
+ () -> out.attributeBytesBase64(/* namespace */ null, /* name */
+ "attributeBytesBase64", testBytes));
+ }
+
+ @Test
+ public void testAttributeBytesHex_MaximumBinaryData() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+ try {
+ out.attributeBytesHex(/* namespace */ null, /* name */ "attributeBytesHex", testBytes);
+ } catch (Exception e) {
+ fail("testAttributeBytesHex fails with exception: " + e.toString());
+ }
+ }
+
+ @Test
+ public void testAttributeBytesBase64_MaximumBinaryData() throws Exception {
+ final TypedXmlSerializer out = Xml.newBinarySerializer();
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ out.setOutput(os, StandardCharsets.UTF_8.name());
+
+ final byte[] testBytes = new byte[MAX_UNSIGNED_SHORT];
+ try {
+ out.attributeBytesBase64(/* namespace */ null, /* name */ "attributeBytesBase64",
+ testBytes);
+ } catch (Exception e) {
+ fail("testAttributeBytesBase64 fails with exception: " + e.toString());
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
index ee740fb..9114c7a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CrossActivityBackAnimation.kt
@@ -27,6 +27,7 @@
import android.graphics.Rect
import android.graphics.RectF
import android.os.RemoteException
+import android.util.TimeUtils
import android.view.Choreographer
import android.view.Display
import android.view.IRemoteAnimationFinishedCallback
@@ -109,6 +110,7 @@
private val postCommitFlingSpring = SpringForce(SPRING_SCALE)
.setStiffness(SpringForce.STIFFNESS_LOW)
.setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)
+ protected var gestureProgress = 0f
/** Background color to be used during the animation, also see [getBackgroundColor] */
protected var customizedBackgroundColor = 0
@@ -212,6 +214,7 @@
private fun onGestureProgress(backEvent: BackEvent) {
val progress = gestureInterpolator.getInterpolation(backEvent.progress)
+ gestureProgress = progress
currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
val yOffset = getYOffset(currentClosingRect, backEvent.touchY)
currentClosingRect.offset(0f, yOffset)
@@ -257,12 +260,16 @@
}
// kick off spring animation with the current velocity from the pre-commit phase, this
- // affects the scaling of the closing activity during post-commit
+ // affects the scaling of the closing and/or opening activity during post-commit
+ val startVelocity =
+ if (gestureProgress < 0.1f) -DEFAULT_FLING_VELOCITY else -velocity * SPRING_SCALE
val flingAnimation = SpringAnimation(postCommitFlingScale, SPRING_SCALE)
- .setStartVelocity(min(0f, -velocity * SPRING_SCALE))
+ .setStartVelocity(startVelocity.coerceIn(-MAX_FLING_VELOCITY, 0f))
.setStartValue(SPRING_SCALE)
.setSpring(postCommitFlingSpring)
flingAnimation.start()
+ // do an animation-frame immediately to prevent idle frame
+ flingAnimation.doAnimationFrame(choreographer.lastFrameTimeNanos / TimeUtils.NANOS_PER_MS)
val valueAnimator =
ValueAnimator.ofFloat(1f, 0f).setDuration(getPostCommitAnimationDuration())
@@ -292,6 +299,7 @@
enteringTarget?.let {
if (it.leash != null && it.leash.isValid) {
transaction.setCornerRadius(it.leash, 0f)
+ if (!triggerBack) transaction.setAlpha(it.leash, 0f)
it.leash.release()
}
enteringTarget = null
@@ -315,19 +323,22 @@
isLetterboxed = false
enteringHasSameLetterbox = false
lastPostCommitFlingScale = SPRING_SCALE
+ gestureProgress = 0f
}
protected fun applyTransform(
leash: SurfaceControl?,
rect: RectF,
alpha: Float,
- baseTransformation: Transformation? = null
+ baseTransformation: Transformation? = null,
+ flingMode: FlingMode = FlingMode.NO_FLING
) {
if (leash == null || !leash.isValid) return
tempRectF.set(rect)
- if (leash == closingTarget?.leash) {
- lastPostCommitFlingScale = (postCommitFlingScale.value / SPRING_SCALE).coerceIn(
- minimumValue = MAX_FLING_SCALE, maximumValue = lastPostCommitFlingScale
+ if (flingMode != FlingMode.NO_FLING) {
+ lastPostCommitFlingScale = min(
+ postCommitFlingScale.value / SPRING_SCALE,
+ if (flingMode == FlingMode.FLING_BOUNCE) 1f else lastPostCommitFlingScale
)
// apply an additional scale to the closing target to account for fling velocity
tempRectF.scaleCentered(lastPostCommitFlingScale)
@@ -529,14 +540,30 @@
private const val MAX_SCRIM_ALPHA_DARK = 0.8f
private const val MAX_SCRIM_ALPHA_LIGHT = 0.2f
private const val SPRING_SCALE = 100f
- private const val MAX_FLING_SCALE = 0.6f
+ private const val MAX_FLING_VELOCITY = 1000f
+ private const val DEFAULT_FLING_VELOCITY = 120f
+ }
+
+ enum class FlingMode {
+ NO_FLING,
+
+ /**
+ * This is used for the closing target in custom cross-activity back animations. When the
+ * back gesture is flung, the closing target shrinks a bit further with a spring motion.
+ */
+ FLING_SHRINK,
+
+ /**
+ * This is used for the closing and opening target in the default cross-activity back
+ * animation. When the back gesture is flung, the closing and opening targets shrink a
+ * bit further and then bounce back with a spring motion.
+ */
+ FLING_BOUNCE
}
}
// The target will loose focus when alpha == 0, so keep a minimum value for it.
-private fun keepMinimumAlpha(transAlpha: Float): Float {
- return max(transAlpha.toDouble(), 0.005).toFloat()
-}
+private fun keepMinimumAlpha(transAlpha: Float) = max(transAlpha, 0.005f)
private fun isDarkMode(context: Context): Boolean {
return context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK ==
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
index c4aafea..c738ce5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/CustomCrossActivityBackAnimation.kt
@@ -57,7 +57,6 @@
private var enterAnimation: Animation? = null
private var closeAnimation: Animation? = null
private val transformation = Transformation()
- private var gestureProgress = 0f
override val allowEnteringYShift = false
@@ -105,7 +104,6 @@
}
override fun getPreCommitEnteringBaseTransformation(progress: Float): Transformation {
- gestureProgress = progress
transformation.clear()
enterAnimation!!.getTransformationAt(progress * PRE_COMMIT_MAX_PROGRESS, transformation)
return transformation
@@ -134,7 +132,13 @@
if (closingTarget == null || enteringTarget == null) return
val closingProgress = closeAnimation!!.getPostCommitProgress(linearProgress)
- applyTransform(closingTarget!!.leash, currentClosingRect, closingProgress, closeAnimation!!)
+ applyTransform(
+ closingTarget!!.leash,
+ currentClosingRect,
+ closingProgress,
+ closeAnimation!!,
+ FlingMode.FLING_SHRINK
+ )
val enteringProgress = MathUtils.lerp(
gestureProgress * PRE_COMMIT_MAX_PROGRESS,
1f,
@@ -144,7 +148,8 @@
enteringTarget!!.leash,
currentEnteringRect,
enteringProgress,
- enterAnimation!!
+ enterAnimation!!,
+ FlingMode.NO_FLING
)
applyTransaction()
}
@@ -153,11 +158,12 @@
leash: SurfaceControl,
rect: RectF,
progress: Float,
- animation: Animation
+ animation: Animation,
+ flingMode: FlingMode
) {
transformation.clear()
animation.getTransformationAt(progress, transformation)
- applyTransform(leash, rect, transformation.alpha, transformation)
+ applyTransform(leash, rect, transformation.alpha, transformation, flingMode)
}
override fun finishAnimation() {
@@ -166,7 +172,6 @@
enterAnimation?.reset()
enterAnimation = null
transformation.clear()
- gestureProgress = 0f
super.finishAnimation()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
index 44752fe..3b5eb36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/DefaultCrossActivityBackAnimation.kt
@@ -43,7 +43,7 @@
Choreographer.getInstance()
) {
- private val postCommitInterpolator = Interpolators.FAST_OUT_SLOW_IN
+ private val postCommitInterpolator = Interpolators.EMPHASIZED
private val enteringStartOffset =
context.resources.getDimension(R.dimen.cross_activity_back_entering_start_offset)
override val allowEnteringYShift = true
@@ -87,17 +87,27 @@
override fun onPostCommitProgress(linearProgress: Float) {
super.onPostCommitProgress(linearProgress)
- val closingAlpha = max(1f - linearProgress * 2, 0f)
+ val closingAlpha = max(1f - linearProgress * 5, 0f)
val progress = postCommitInterpolator.getInterpolation(linearProgress)
currentClosingRect.setInterpolatedRectF(startClosingRect, targetClosingRect, progress)
- applyTransform(closingTarget?.leash, currentClosingRect, closingAlpha)
+ applyTransform(
+ closingTarget?.leash,
+ currentClosingRect,
+ closingAlpha,
+ flingMode = FlingMode.FLING_BOUNCE
+ )
currentEnteringRect.setInterpolatedRectF(startEnteringRect, targetEnteringRect, progress)
- applyTransform(enteringTarget?.leash, currentEnteringRect, 1f)
+ applyTransform(
+ enteringTarget?.leash,
+ currentEnteringRect,
+ 1f,
+ flingMode = FlingMode.FLING_BOUNCE
+ )
applyTransaction()
}
companion object {
- private const val POST_COMMIT_DURATION = 300L
+ private const val POST_COMMIT_DURATION = 450L
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
index c79eef7e..cd478e5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java
@@ -143,6 +143,10 @@
}
public static boolean handles(TransitionInfo info) {
+ // There is no animation for screen-wake unless we are immediately unlocking.
+ if (info.getType() == WindowManager.TRANSIT_WAKE && !info.isKeyguardGoingAway()) {
+ return false;
+ }
return (info.getFlags() & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0;
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
index 71072a5..d91c7e6 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CopyableBodyTest.kt
@@ -16,9 +16,9 @@
package com.android.settingslib.spa.widget.ui
-import android.content.ClipData
-import android.content.ClipboardManager
import android.content.Context
+import androidx.compose.ui.platform.ClipboardManager
+import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.longClick
@@ -63,9 +63,9 @@
@Test
fun onCopy_saveToClipboard() {
- val clipboardManager = context.getSystemService(ClipboardManager::class.java)!!
- clipboardManager.setPrimaryClip(ClipData.newPlainText("", ""))
+ var clipboardManager: ClipboardManager? = null
composeTestRule.setContent {
+ clipboardManager = LocalClipboardManager.current
CopyableBody(TEXT)
}
@@ -74,7 +74,7 @@
}
composeTestRule.onNodeWithText(context.getString(android.R.string.copy)).performClick()
- assertThat(clipboardManager.primaryClip!!.getItemAt(0).text.toString()).isEqualTo(TEXT)
+ assertThat(clipboardManager?.getText()?.text).isEqualTo(TEXT)
}
private companion object {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index a90f82e..c329384 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -1,9 +1,16 @@
package com.android.systemui.communal.ui.compose
+import androidx.compose.animation.core.LinearEasing
+import androidx.compose.animation.core.RepeatMode
+import androidx.compose.animation.core.animateFloat
+import androidx.compose.animation.core.infiniteRepeatable
+import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
+import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
@@ -13,12 +20,20 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.composed
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.CommunalSwipeDetector
@@ -36,8 +51,10 @@
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.observableTransitionState
import com.android.compose.animation.scene.transitions
+import com.android.compose.theme.LocalAndroidColorScheme
import com.android.systemui.Flags
import com.android.systemui.Flags.glanceableHubFullscreenSwipe
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalTransitionKeys
import com.android.systemui.communal.ui.compose.extensions.allowGestures
@@ -102,6 +119,10 @@
val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle(initialValue = false)
val showGestureIndicator by
viewModel.showGestureIndicator.collectAsStateWithLifecycle(initialValue = false)
+ val backgroundType by
+ viewModel.communalBackground.collectAsStateWithLifecycle(
+ initialValue = CommunalBackgroundType.DEFAULT
+ )
val state: MutableSceneTransitionLayoutState = remember {
MutableSceneTransitionLayoutState(
initialScene = currentSceneKey,
@@ -174,7 +195,7 @@
userActions =
mapOf(Swipe(SwipeDirection.Right, fromSource = Edge.Left) to CommunalScenes.Blank)
) {
- CommunalScene(colors, content)
+ CommunalScene(backgroundType, colors, content)
}
}
@@ -186,17 +207,87 @@
/** Scene containing the glanceable hub UI. */
@Composable
private fun SceneScope.CommunalScene(
+ backgroundType: CommunalBackgroundType,
colors: CommunalColors,
content: CommunalContent,
modifier: Modifier = Modifier,
) {
- val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
-
- Box(
- modifier =
- Modifier.element(Communal.Elements.Scrim)
- .fillMaxSize()
- .background(Color(backgroundColor.toArgb())),
- )
+ Box(modifier = Modifier.element(Communal.Elements.Scrim).fillMaxSize()) {
+ when (backgroundType) {
+ CommunalBackgroundType.DEFAULT -> DefaultBackground(colors = colors)
+ CommunalBackgroundType.STATIC_GRADIENT -> StaticLinearGradient()
+ CommunalBackgroundType.ANIMATED -> AnimatedLinearGradient()
+ }
+ }
with(content) { Content(modifier = modifier) }
}
+
+/** Default background of the hub, a single color */
+@Composable
+private fun BoxScope.DefaultBackground(
+ colors: CommunalColors,
+) {
+ val backgroundColor by colors.backgroundColor.collectAsStateWithLifecycle()
+ Box(
+ modifier = Modifier.matchParentSize().background(Color(backgroundColor.toArgb())),
+ )
+}
+
+/** Experimental hub background, static linear gradient */
+@Composable
+private fun BoxScope.StaticLinearGradient() {
+ val colors = LocalAndroidColorScheme.current
+ Box(
+ Modifier.matchParentSize()
+ .background(
+ Brush.linearGradient(colors = listOf(colors.primary, colors.primaryContainer)),
+ )
+ )
+ BackgroundTopScrim()
+}
+
+/** Experimental hub background, animated linear gradient */
+@Composable
+private fun BoxScope.AnimatedLinearGradient() {
+ val colors = LocalAndroidColorScheme.current
+ Box(
+ Modifier.matchParentSize()
+ .animatedGradientBackground(colors = listOf(colors.primary, colors.primaryContainer))
+ )
+ BackgroundTopScrim()
+}
+
+/** Scrim placed on top of the background in order to dim/bright colors */
+@Composable
+private fun BoxScope.BackgroundTopScrim() {
+ val darkTheme = isSystemInDarkTheme()
+ val scrimOnTopColor = if (darkTheme) Color.Black else Color.White
+ Box(Modifier.matchParentSize().alpha(0.34f).background(scrimOnTopColor))
+}
+
+/** Modifier which sets the background of a composable to an animated gradient */
+@Composable
+private fun Modifier.animatedGradientBackground(colors: List<Color>): Modifier = composed {
+ var size by remember { mutableStateOf(IntSize.Zero) }
+ val transition = rememberInfiniteTransition(label = "scrim background")
+ val startOffsetX by
+ transition.animateFloat(
+ initialValue = -size.width.toFloat(),
+ targetValue = size.width.toFloat(),
+ animationSpec =
+ infiniteRepeatable(
+ animation = tween(durationMillis = 5_000, easing = LinearEasing),
+ repeatMode = RepeatMode.Reverse,
+ ),
+ label = "scrim start offset"
+ )
+ background(
+ brush =
+ Brush.linearGradient(
+ colors = colors,
+ start = Offset(startOffsetX, 0f),
+ end = Offset(startOffsetX + size.width.toFloat(), size.height.toFloat()),
+ )
+ )
+ .onGloballyPositioned { size = it.size }
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
index da29d58..48af8cd 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/SpatialAudioModule.kt
@@ -16,16 +16,13 @@
package com.android.systemui.volume.panel.component.spatialaudio
-import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
import com.android.systemui.volume.panel.component.shared.model.VolumePanelComponents
import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria
-import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
-import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioPopup
+import com.android.systemui.volume.panel.component.spatialaudio.ui.composable.SpatialAudioComponent
import com.android.systemui.volume.panel.domain.ComponentAvailabilityCriteria
import com.android.systemui.volume.panel.shared.model.VolumePanelUiComponent
import dagger.Binds
import dagger.Module
-import dagger.Provides
import dagger.multibindings.IntoMap
import dagger.multibindings.StringKey
@@ -40,14 +37,8 @@
criteria: SpatialAudioAvailabilityCriteria
): ComponentAvailabilityCriteria
- companion object {
-
- @Provides
- @IntoMap
- @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
- fun provideVolumePanelUiComponent(
- viewModel: SpatialAudioViewModel,
- popup: SpatialAudioPopup,
- ): VolumePanelUiComponent = ButtonComponent(viewModel.spatialAudioButton, popup::show)
- }
+ @Binds
+ @IntoMap
+ @StringKey(VolumePanelComponents.SPATIAL_AUDIO)
+ fun bindVolumePanelUiComponent(component: SpatialAudioComponent): VolumePanelUiComponent
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt
new file mode 100644
index 0000000..2d89b5c
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioComponent.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.spatialaudio.ui.composable
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
+import com.android.systemui.volume.panel.component.button.ui.composable.ButtonComponent
+import com.android.systemui.volume.panel.component.button.ui.composable.ToggleButtonComponent
+import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel
+import com.android.systemui.volume.panel.component.spatial.ui.viewmodel.SpatialAudioViewModel
+import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent
+import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope
+import javax.inject.Inject
+
+/** [ComposeVolumePanelUiComponent] that represents spatial audio button in the Volume Panel. */
+class SpatialAudioComponent
+@Inject
+constructor(
+ private val viewModel: SpatialAudioViewModel,
+ private val popup: SpatialAudioPopup,
+) : ComposeVolumePanelUiComponent {
+
+ @Composable
+ override fun VolumePanelComposeScope.Content(modifier: Modifier) {
+ val shouldUsePopup by viewModel.shouldUsePopup.collectAsStateWithLifecycle()
+
+ val buttonComponent: ComposeVolumePanelUiComponent =
+ remember(shouldUsePopup) {
+ if (shouldUsePopup) {
+ ButtonComponent(viewModel.spatialAudioButton, popup::show)
+ } else {
+ ToggleButtonComponent(viewModel.spatialAudioButton) {
+ if (it) {
+ viewModel.setEnabled(SpatialAudioEnabledModel.SpatialAudioEnabled)
+ } else {
+ viewModel.setEnabled(SpatialAudioEnabledModel.Disabled)
+ }
+ }
+ }
+ }
+ with(buttonComponent) { Content(modifier) }
+ }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
index 5d1a7c5..7fd3a176 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt
@@ -27,11 +27,12 @@
import androidx.compose.runtime.snapshotFlow
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.lerp
+import androidx.compose.ui.graphics.colorspace.ColorSpaces
import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastCoerceIn
-import androidx.compose.ui.util.lerp
+import androidx.compose.ui.util.fastLastOrNull
+import kotlin.math.roundToInt
/**
* A [State] whose [value] is animated.
@@ -74,7 +75,7 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Int> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+ return animateSceneValueAsState(value, key, SharedIntType, canOverflow)
}
/**
@@ -88,7 +89,19 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Int> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow)
+ return animateElementValueAsState(value, key, SharedIntType, canOverflow)
+}
+
+private object SharedIntType : SharedValueType<Int, Int> {
+ override val unspecifiedValue: Int = Int.MIN_VALUE
+ override val zeroDeltaValue: Int = 0
+
+ override fun lerp(a: Int, b: Int, progress: Float): Int =
+ androidx.compose.ui.util.lerp(a, b, progress)
+
+ override fun diff(a: Int, b: Int): Int = a - b
+
+ override fun addWeighted(a: Int, b: Int, bWeight: Float): Int = (a + b * bWeight).roundToInt()
}
/**
@@ -102,7 +115,7 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Float> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+ return animateSceneValueAsState(value, key, SharedFloatType, canOverflow)
}
/**
@@ -116,7 +129,19 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Float> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow)
+ return animateElementValueAsState(value, key, SharedFloatType, canOverflow)
+}
+
+private object SharedFloatType : SharedValueType<Float, Float> {
+ override val unspecifiedValue: Float = Float.MIN_VALUE
+ override val zeroDeltaValue: Float = 0f
+
+ override fun lerp(a: Float, b: Float, progress: Float): Float =
+ androidx.compose.ui.util.lerp(a, b, progress)
+
+ override fun diff(a: Float, b: Float): Float = a - b
+
+ override fun addWeighted(a: Float, b: Float, bWeight: Float): Float = a + b * bWeight
}
/**
@@ -130,7 +155,7 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Dp> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow)
+ return animateSceneValueAsState(value, key, SharedDpType, canOverflow)
}
/**
@@ -144,7 +169,20 @@
key: ValueKey,
canOverflow: Boolean = true,
): AnimatedState<Dp> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow)
+ return animateElementValueAsState(value, key, SharedDpType, canOverflow)
+}
+
+private object SharedDpType : SharedValueType<Dp, Dp> {
+ override val unspecifiedValue: Dp = Dp.Unspecified
+ override val zeroDeltaValue: Dp = 0.dp
+
+ override fun lerp(a: Dp, b: Dp, progress: Float): Dp {
+ return androidx.compose.ui.unit.lerp(a, b, progress)
+ }
+
+ override fun diff(a: Dp, b: Dp): Dp = a - b
+
+ override fun addWeighted(a: Dp, b: Dp, bWeight: Float): Dp = a + b * bWeight
}
/**
@@ -157,7 +195,7 @@
value: Color,
key: ValueKey,
): AnimatedState<Color> {
- return animateSceneValueAsState(value, key, ::lerp, canOverflow = false)
+ return animateSceneValueAsState(value, key, SharedColorType, canOverflow = false)
}
/**
@@ -170,9 +208,56 @@
value: Color,
key: ValueKey,
): AnimatedState<Color> {
- return animateElementValueAsState(value, key, ::lerp, canOverflow = false)
+ return animateElementValueAsState(value, key, SharedColorType, canOverflow = false)
}
+private object SharedColorType : SharedValueType<Color, ColorDelta> {
+ override val unspecifiedValue: Color = Color.Unspecified
+ override val zeroDeltaValue: ColorDelta = ColorDelta(0f, 0f, 0f, 0f)
+
+ override fun lerp(a: Color, b: Color, progress: Float): Color {
+ return androidx.compose.ui.graphics.lerp(a, b, progress)
+ }
+
+ override fun diff(a: Color, b: Color): ColorDelta {
+ // Similar to lerp, we convert colors to the Oklab color space to perform operations on
+ // colors.
+ val aOklab = a.convert(ColorSpaces.Oklab)
+ val bOklab = b.convert(ColorSpaces.Oklab)
+ return ColorDelta(
+ red = aOklab.red - bOklab.red,
+ green = aOklab.green - bOklab.green,
+ blue = aOklab.blue - bOklab.blue,
+ alpha = aOklab.alpha - bOklab.alpha,
+ )
+ }
+
+ override fun addWeighted(a: Color, b: ColorDelta, bWeight: Float): Color {
+ val aOklab = a.convert(ColorSpaces.Oklab)
+ return Color(
+ red = aOklab.red + b.red * bWeight,
+ green = aOklab.green + b.green * bWeight,
+ blue = aOklab.blue + b.blue * bWeight,
+ alpha = aOklab.alpha + b.alpha * bWeight,
+ colorSpace = ColorSpaces.Oklab,
+ )
+ .convert(aOklab.colorSpace)
+ }
+}
+
+/**
+ * Represents the diff between two colors in the same color space.
+ *
+ * Note: This class is necessary because Color() checks the bounds of its values and UncheckedColor
+ * is internal.
+ */
+private class ColorDelta(
+ val red: Float,
+ val green: Float,
+ val blue: Float,
+ val alpha: Float,
+)
+
@Composable
internal fun <T> animateSharedValueAsState(
layoutImpl: SceneTransitionLayoutImpl,
@@ -180,23 +265,22 @@
element: ElementKey?,
key: ValueKey,
value: T,
- lerp: (T, T, Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean,
): AnimatedState<T> {
DisposableEffect(layoutImpl, scene, element, key) {
// Create the associated maps that hold the current value for each (element, scene) pair.
val valueMap = layoutImpl.sharedValues.getOrPut(key) { mutableMapOf() }
- val sceneToValueMap =
- valueMap.getOrPut(element) { SnapshotStateMap<SceneKey, Any>() }
- as SnapshotStateMap<SceneKey, T>
- sceneToValueMap[scene] = value
+ val sharedValue = valueMap.getOrPut(element) { SharedValue(type) } as SharedValue<T, *>
+ val targetValues = sharedValue.targetValues
+ targetValues[scene] = value
onDispose {
// Remove the value associated to the current scene, and eventually remove the maps if
// they are empty.
- sceneToValueMap.remove(scene)
+ targetValues.remove(scene)
- if (sceneToValueMap.isEmpty() && valueMap[element] === sceneToValueMap) {
+ if (targetValues.isEmpty() && valueMap[element] === sharedValue) {
valueMap.remove(element)
if (valueMap.isEmpty() && layoutImpl.sharedValues[key] === valueMap) {
@@ -208,34 +292,25 @@
// Update the current value. Note that side effects run after disposable effects, so we know
// that the associated maps were created at this point.
- SideEffect { sceneToValueMap<T>(layoutImpl, key, element)[scene] = value }
-
- return remember(layoutImpl, scene, element, lerp, canOverflow) {
- object : AnimatedState<T> {
- override val value: T
- get() = value(layoutImpl, scene, element, key, lerp, canOverflow)
-
- @Composable
- override fun unsafeCompositionState(initialValue: T): State<T> {
- val state = remember { mutableStateOf(initialValue) }
-
- val animatedState = this
- LaunchedEffect(animatedState) {
- snapshotFlow { animatedState.value }.collect { state.value = it }
- }
-
- return state
- }
+ SideEffect {
+ if (value == type.unspecifiedValue) {
+ error("value is equal to $value, which is the undefined value for this type.")
}
+
+ sharedValue<T, Any>(layoutImpl, key, element).targetValues[scene] = value
+ }
+
+ return remember(layoutImpl, scene, element, canOverflow) {
+ AnimatedStateImpl<T, Any>(layoutImpl, scene, element, key, canOverflow)
}
}
-private fun <T> sceneToValueMap(
+private fun <T, Delta> sharedValue(
layoutImpl: SceneTransitionLayoutImpl,
key: ValueKey,
element: ElementKey?
-): MutableMap<SceneKey, T> {
- return layoutImpl.sharedValues[key]?.get(element)?.let { it as SnapshotStateMap<SceneKey, T> }
+): SharedValue<T, Delta> {
+ return layoutImpl.sharedValues[key]?.get(element)?.let { it as SharedValue<T, Delta> }
?: error(valueReadTooEarlyMessage(key))
}
@@ -244,62 +319,155 @@
"means that you are reading it during composition, which you should not do. See the " +
"documentation of AnimatedState for more information."
-private fun <T> value(
- layoutImpl: SceneTransitionLayoutImpl,
- scene: SceneKey,
- element: ElementKey?,
- key: ValueKey,
- lerp: (T, T, Float) -> T,
- canOverflow: Boolean,
-): T {
- return valueOrNull(layoutImpl, scene, element, key, lerp, canOverflow)
- ?: error(valueReadTooEarlyMessage(key))
+internal class SharedValue<T, Delta>(
+ val type: SharedValueType<T, Delta>,
+) {
+ /** The target value of this shared value for each scene. */
+ val targetValues = SnapshotStateMap<SceneKey, T>()
+
+ /** The last value of this shared value. */
+ var lastValue: T = type.unspecifiedValue
+
+ /** The value of this shared value before the last interruption (if any). */
+ var valueBeforeInterruption: T = type.unspecifiedValue
+
+ /** The delta value to add to this shared value to have smoother interruptions. */
+ var valueInterruptionDelta = type.zeroDeltaValue
+
+ /** The last transition that was used when the value of this shared state. */
+ var lastTransition: TransitionState.Transition? = null
}
-private fun <T> valueOrNull(
- layoutImpl: SceneTransitionLayoutImpl,
- scene: SceneKey,
- element: ElementKey?,
- key: ValueKey,
- lerp: (T, T, Float) -> T,
- canOverflow: Boolean,
-): T? {
- val sceneToValueMap = sceneToValueMap<T>(layoutImpl, key, element)
- fun sceneValue(scene: SceneKey): T? = sceneToValueMap[scene]
+private class AnimatedStateImpl<T, Delta>(
+ private val layoutImpl: SceneTransitionLayoutImpl,
+ private val scene: SceneKey,
+ private val element: ElementKey?,
+ private val key: ValueKey,
+ private val canOverflow: Boolean,
+) : AnimatedState<T> {
+ override val value: T
+ get() = value()
- return when (val transition = layoutImpl.state.transitionState) {
- is TransitionState.Idle -> sceneValue(transition.currentScene)
- is TransitionState.Transition -> {
- // Note: no need to check for transition ready here given that all target values are
- // defined during composition, we should already have the correct values to interpolate
- // between here.
- val fromValue = sceneValue(transition.fromScene)
- val toValue = sceneValue(transition.toScene)
- if (fromValue != null && toValue != null) {
- if (fromValue == toValue) {
- // Optimization: avoid reading progress if the values are the same, so we don't
- // relayout/redraw for nothing.
- fromValue
- } else {
- // In the case of bouncing, if the value remains constant during the overscroll,
- // we should use the value of the scene we are bouncing around.
- if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
- val bouncingScene = transition.bouncingScene
- if (bouncingScene != null) {
- return sceneValue(bouncingScene)
- }
+ private fun value(): T {
+ val sharedValue = sharedValue<T, Delta>(layoutImpl, key, element)
+ val transition = transition(sharedValue)
+ val value: T =
+ valueOrNull(sharedValue, transition)
+ // TODO(b/311600838): Remove this. We should not have to fallback to the current
+ // scene value, but we have to because code of removed nodes can still run if they
+ // are placed with a graphics layer.
+ ?: sharedValue[scene]
+ ?: error(valueReadTooEarlyMessage(key))
+ val interruptedValue = computeInterruptedValue(sharedValue, transition, value)
+ sharedValue.lastValue = interruptedValue
+ return interruptedValue
+ }
+
+ private operator fun SharedValue<T, *>.get(scene: SceneKey): T? = targetValues[scene]
+
+ private fun valueOrNull(
+ sharedValue: SharedValue<T, *>,
+ transition: TransitionState.Transition?,
+ ): T? {
+ if (transition == null) {
+ return sharedValue[layoutImpl.state.transitionState.currentScene]
+ }
+
+ val fromValue = sharedValue[transition.fromScene]
+ val toValue = sharedValue[transition.toScene]
+ return if (fromValue != null && toValue != null) {
+ if (fromValue == toValue) {
+ // Optimization: avoid reading progress if the values are the same, so we don't
+ // relayout/redraw for nothing.
+ fromValue
+ } else {
+ // In the case of bouncing, if the value remains constant during the overscroll, we
+ // should use the value of the scene we are bouncing around.
+ if (!canOverflow && transition is TransitionState.HasOverscrollProperties) {
+ val bouncingScene = transition.bouncingScene
+ if (bouncingScene != null) {
+ return sharedValue[bouncingScene]
}
-
- val progress =
- if (canOverflow) transition.progress
- else transition.progress.fastCoerceIn(0f, 1f)
- lerp(fromValue, toValue, progress)
}
- } else fromValue ?: toValue
+
+ val progress =
+ if (canOverflow) transition.progress
+ else transition.progress.fastCoerceIn(0f, 1f)
+ sharedValue.type.lerp(fromValue, toValue, progress)
+ }
+ } else fromValue ?: toValue
+ }
+
+ private fun transition(sharedValue: SharedValue<T, Delta>): TransitionState.Transition? {
+ val targetValues = sharedValue.targetValues
+ val transition =
+ if (element != null) {
+ layoutImpl.elements[element]?.sceneStates?.let { sceneStates ->
+ layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
+ transition.fromScene in sceneStates || transition.toScene in sceneStates
+ }
+ }
+ } else {
+ layoutImpl.state.currentTransitions.fastLastOrNull { transition ->
+ transition.fromScene in targetValues || transition.toScene in targetValues
+ }
+ }
+
+ val previousTransition = sharedValue.lastTransition
+ sharedValue.lastTransition = transition
+
+ if (transition != previousTransition && transition != null && previousTransition != null) {
+ // The previous transition was interrupted by another transition.
+ sharedValue.valueBeforeInterruption = sharedValue.lastValue
+ sharedValue.valueInterruptionDelta = sharedValue.type.zeroDeltaValue
+ } else if (transition == null && previousTransition != null) {
+ // The transition was just finished.
+ sharedValue.valueBeforeInterruption = sharedValue.type.unspecifiedValue
+ sharedValue.valueInterruptionDelta = sharedValue.type.zeroDeltaValue
+ }
+
+ return transition
+ }
+
+ /**
+ * Compute what [value] should be if we take the
+ * [interruption progress][TransitionState.Transition.interruptionProgress] of [transition] into
+ * account.
+ */
+ private fun computeInterruptedValue(
+ sharedValue: SharedValue<T, Delta>,
+ transition: TransitionState.Transition?,
+ value: T,
+ ): T {
+ val type = sharedValue.type
+ if (sharedValue.valueBeforeInterruption != type.unspecifiedValue) {
+ sharedValue.valueInterruptionDelta =
+ type.diff(sharedValue.valueBeforeInterruption, value)
+ sharedValue.valueBeforeInterruption = type.unspecifiedValue
+ }
+
+ val delta = sharedValue.valueInterruptionDelta
+ return if (delta == type.zeroDeltaValue || transition == null) {
+ value
+ } else {
+ val interruptionProgress = transition.interruptionProgress(layoutImpl)
+ if (interruptionProgress == 0f) {
+ value
+ } else {
+ type.addWeighted(value, delta, interruptionProgress)
+ }
}
}
- // TODO(b/311600838): Remove this. We should not have to fallback to the current scene value,
- // but we have to because code of removed nodes can still run if they are placed with a graphics
- // layer.
- ?: sceneValue(scene)
+
+ @Composable
+ override fun unsafeCompositionState(initialValue: T): State<T> {
+ val state = remember { mutableStateOf(initialValue) }
+
+ val animatedState = this
+ LaunchedEffect(animatedState) {
+ snapshotFlow { animatedState.value }.collect { state.value = it }
+ }
+
+ return state
+ }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 1f81245..e9633c2 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -148,9 +148,9 @@
val swipes = computeSwipes(fromScene, startedPosition, pointersDown)
val result =
swipes.findUserActionResult(fromScene, overSlop, true)
- // As we were unable to locate a valid target scene, the initial SwipeTransition
- // cannot be defined. Consequently, a simple NoOp Controller will be returned.
- ?: return NoOpDragController
+ // As we were unable to locate a valid target scene, the initial SwipeTransition
+ // cannot be defined. Consequently, a simple NoOp Controller will be returned.
+ ?: return NoOpDragController
return updateDragController(
swipes = swipes,
@@ -521,6 +521,7 @@
}
return SwipeTransition(
+ layoutImpl = layoutImpl,
layoutState = layoutState,
coroutineScope = coroutineScope,
key = result.transitionKey,
@@ -534,6 +535,7 @@
private fun SwipeTransition(old: SwipeTransition): SwipeTransition {
return SwipeTransition(
+ layoutImpl = old.layoutImpl,
layoutState = old.layoutState,
coroutineScope = old.coroutineScope,
key = old.key,
@@ -550,6 +552,7 @@
}
private class SwipeTransition(
+ val layoutImpl: SceneTransitionLayoutImpl,
val layoutState: BaseSceneTransitionLayoutState,
val coroutineScope: CoroutineScope,
val key: TransitionKey?,
@@ -607,6 +610,12 @@
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
+ override val density: Float
+ get() = layoutImpl.density.density
+
+ override val fontScale: Float
+ get() = layoutImpl.density.fontScale
+
override val absoluteDistance: Float
get() = distance().absoluteValue
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
index 4b20aca..be005ea 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/MovableElement.kt
@@ -77,7 +77,7 @@
override fun <T> animateElementValueAsState(
value: T,
key: ValueKey,
- lerp: (start: T, stop: T, fraction: Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean
): AnimatedState<T> {
return animateSharedValueAsState(
@@ -86,7 +86,7 @@
element,
key,
value,
- lerp,
+ type,
canOverflow,
)
}
@@ -184,8 +184,7 @@
fromSceneZIndex = layoutImpl.scenes.getValue(transition.fromScene).zIndex,
toSceneZIndex = layoutImpl.scenes.getValue(transition.toScene).zIndex,
) != null
- }
- ?: return false
+ } ?: return false
// Always compose movable elements in the scene picked by their scene picker.
return shouldDrawOrComposeSharedElement(
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
index 6fef33c..936f4ba 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Scene.kt
@@ -24,7 +24,6 @@
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.approachLayout
import androidx.compose.ui.platform.testTag
@@ -69,7 +68,6 @@
}
@Composable
- @OptIn(ExperimentalComposeUiApi::class)
fun Content(modifier: Modifier = Modifier) {
Box(
modifier
@@ -96,6 +94,7 @@
private val layoutImpl: SceneTransitionLayoutImpl,
private val scene: Scene,
) : SceneScope, ElementStateScope by layoutImpl.elementStateScope {
+ override val sceneKey: SceneKey = scene.key
override val layoutState: SceneTransitionLayoutState = layoutImpl.state
override fun Modifier.element(key: ElementKey): Modifier {
@@ -124,7 +123,7 @@
override fun <T> animateSceneValueAsState(
value: T,
key: ValueKey,
- lerp: (T, T, Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean
): AnimatedState<T> {
return animateSharedValueAsState(
@@ -133,7 +132,7 @@
element = null,
key = key,
value = value,
- lerp = lerp,
+ type = type,
canOverflow = canOverflow,
)
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index cf8c584..2946b04 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -167,6 +167,9 @@
@Stable
@ElementDsl
interface BaseSceneScope : ElementStateScope {
+ /** The key of this scene. */
+ val sceneKey: SceneKey
+
/** The state of the [SceneTransitionLayout] in which this scene is contained. */
val layoutState: SceneTransitionLayoutState
@@ -285,9 +288,7 @@
*
* @param value the value of this shared value in the current scene.
* @param key the key of this shared value.
- * @param lerp the *linear* interpolation function that should be used to interpolate between
- * two different values. Note that it has to be linear because the [fraction] passed to this
- * interpolator is already interpolated.
+ * @param type the [SharedValueType] of this animated value.
* @param canOverflow whether this value can overflow past the values it is interpolated
* between, for instance because the transition is animated using a bouncy spring.
* @see animateSceneIntAsState
@@ -299,11 +300,39 @@
fun <T> animateSceneValueAsState(
value: T,
key: ValueKey,
- lerp: (start: T, stop: T, fraction: Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean,
): AnimatedState<T>
}
+/**
+ * The type of a shared value animated using [ElementScope.animateElementValueAsState] or
+ * [SceneScope.animateSceneValueAsState].
+ */
+@Stable
+interface SharedValueType<T, Delta> {
+ /** The unspecified value for this type. */
+ val unspecifiedValue: T
+
+ /**
+ * The zero value of this type. It should be equal to what [diff(x, x)] returns for any value of
+ * x.
+ */
+ val zeroDeltaValue: Delta
+
+ /**
+ * Return the linear interpolation of [a] and [b] at the given [progress], i.e. `a + (b - a) *
+ * progress`.
+ */
+ fun lerp(a: T, b: T, progress: Float): T
+
+ /** Return `a - b`. */
+ fun diff(a: T, b: T): Delta
+
+ /** Return `a + b * bWeight`. */
+ fun addWeighted(a: T, b: Delta, bWeight: Float): T
+}
+
@Stable
@ElementDsl
interface ElementScope<ContentScope> {
@@ -312,9 +341,7 @@
*
* @param value the value of this shared value in the current scene.
* @param key the key of this shared value.
- * @param lerp the *linear* interpolation function that should be used to interpolate between
- * two different values. Note that it has to be linear because the [fraction] passed to this
- * interpolator is already interpolated.
+ * @param type the [SharedValueType] of this animated value.
* @param canOverflow whether this value can overflow past the values it is interpolated
* between, for instance because the transition is animated using a bouncy spring.
* @see animateElementIntAsState
@@ -326,7 +353,7 @@
fun <T> animateElementValueAsState(
value: T,
key: ValueKey,
- lerp: (start: T, stop: T, fraction: Float) -> T,
+ type: SharedValueType<T, *>,
canOverflow: Boolean,
): AnimatedState<T>
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index c614265..5fa7c87 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -85,15 +85,14 @@
* The different values of a shared value keyed by a a [ValueKey] and the different elements and
* scenes it is associated to.
*/
- private var _sharedValues:
- MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>? =
+ private var _sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>? =
null
- internal val sharedValues:
- MutableMap<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>
+ internal val sharedValues: MutableMap<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>
get() =
_sharedValues
- ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SnapshotStateMap<SceneKey, *>>>()
- .also { _sharedValues = it }
+ ?: mutableMapOf<ValueKey, MutableMap<ElementKey?, SharedValue<*, *>>>().also {
+ _sharedValues = it
+ }
// TODO(b/317958526): Lazily allocate scene gesture handlers the first time they are needed.
private val horizontalDraggableHandler: DraggableHandlerImpl
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index a5b6d24..44affd9 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -457,7 +457,7 @@
*/
internal fun startTransition(
transition: TransitionState.Transition,
- transitionKey: TransitionKey?,
+ transitionKey: TransitionKey? = null,
chain: Boolean = true,
) {
checkThread()
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
index a4682ff..465a410 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/TransitionDsl.kt
@@ -20,6 +20,7 @@
import androidx.compose.animation.core.SpringSpec
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.TransitionState.HasOverscrollProperties.Companion.DistanceUnspecified
@@ -192,7 +193,7 @@
)
}
-interface OverscrollScope {
+interface OverscrollScope : Density {
/**
* Return the absolute distance between fromScene and toScene, if available, otherwise
* [DistanceUnspecified].
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
index e8854cf..6e8b208 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt
@@ -32,7 +32,12 @@
import androidx.compose.ui.unit.lerp
import androidx.compose.ui.util.lerp
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.android.compose.animation.scene.TestScenes.SceneB
+import com.android.compose.animation.scene.TestScenes.SceneC
+import com.android.compose.animation.scene.TestScenes.SceneD
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.test.runTest
import org.junit.Assert.assertThrows
import org.junit.Rule
import org.junit.Test
@@ -130,8 +135,8 @@
// The transition lasts 64ms = 4 frames.
spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
},
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -189,8 +194,8 @@
// The transition lasts 64ms = 4 frames.
spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
},
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -243,8 +248,8 @@
// The transition lasts 64ms = 4 frames.
spec = tween(durationMillis = 16 * 4, easing = LinearEasing)
},
- fromScene = TestScenes.SceneA,
- toScene = TestScenes.SceneB,
+ fromScene = SceneA,
+ toScene = SceneB,
) {
before {
assertThat(lastValueInFrom).isEqualTo(fromValues)
@@ -381,4 +386,61 @@
}
}
}
+
+ @Test
+ fun animatedValueIsUsingLastTransition() = runTest {
+ val state =
+ rule.runOnUiThread { MutableSceneTransitionLayoutStateImpl(SceneA, transitions {}) }
+
+ val foo = ValueKey("foo")
+ val bar = ValueKey("bar")
+ val lastValues = mutableMapOf<ValueKey, MutableMap<SceneKey, Float>>()
+
+ @Composable
+ fun SceneScope.animateFloat(value: Float, key: ValueKey) {
+ val animatedValue = animateSceneFloatAsState(value, key)
+ LaunchedEffect(animatedValue) {
+ snapshotFlow { animatedValue.value }
+ .collect { lastValues.getOrPut(key) { mutableMapOf() }[sceneKey] = it }
+ }
+ }
+
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ // foo goes from 0f to 100f in A => B.
+ scene(SceneA) { animateFloat(0f, foo) }
+ scene(SceneB) { animateFloat(100f, foo) }
+
+ // bar goes from 0f to 10f in C => D.
+ scene(SceneC) { animateFloat(0f, bar) }
+ scene(SceneD) { animateFloat(10f, bar) }
+ }
+ }
+
+ rule.runOnUiThread {
+ // A => B is at 30%.
+ state.startTransition(
+ transition(
+ from = SceneA,
+ to = SceneB,
+ progress = { 0.3f },
+ onFinish = neverFinish(),
+ )
+ )
+
+ // C => D is at 70%.
+ state.startTransition(transition(from = SceneC, to = SceneD, progress = { 0.7f }))
+ }
+ rule.waitForIdle()
+
+ assertThat(lastValues[foo]?.get(SceneA)).isWithin(0.001f).of(30f)
+ assertThat(lastValues[foo]?.get(SceneB)).isWithin(0.001f).of(30f)
+ assertThat(lastValues[foo]?.get(SceneC)).isNull()
+ assertThat(lastValues[foo]?.get(SceneD)).isNull()
+
+ assertThat(lastValues[bar]?.get(SceneA)).isNull()
+ assertThat(lastValues[bar]?.get(SceneB)).isNull()
+ assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f)
+ assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index 9692fae..beb74bc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -59,6 +59,7 @@
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.lerp
+import androidx.compose.ui.util.lerp
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.compose.animation.scene.TestScenes.SceneA
import com.android.compose.animation.scene.TestScenes.SceneB
@@ -348,7 +349,7 @@
),
onLayoutImpl = { nullableLayoutImpl = it },
) {
- scene(SceneA) { /* Nothing */}
+ scene(SceneA) { /* Nothing */ }
scene(SceneB) { Box(Modifier.element(key)) }
scene(SceneC) {
when (sceneCState) {
@@ -1083,10 +1084,17 @@
}
val layoutSize = DpSize(200.dp, 100.dp)
+ val lastValues = mutableMapOf<SceneKey, Float>()
@Composable
- fun SceneScope.Foo(size: Dp, modifier: Modifier = Modifier) {
- Box(modifier.element(TestElements.Foo).size(size))
+ fun SceneScope.Foo(size: Dp, value: Float, modifier: Modifier = Modifier) {
+ val sceneKey = this.sceneKey
+ Element(TestElements.Foo, modifier.size(size)) {
+ val animatedValue = animateElementFloatAsState(value, TestValues.Value1)
+ LaunchedEffect(animatedValue) {
+ snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it }
+ }
+ }
}
// The size of Foo when idle in A, B or C.
@@ -1094,6 +1102,11 @@
val sizeInB = 30.dp
val sizeInC = 50.dp
+ // The target value when idle in A, B, or C.
+ val valueInA = 0f
+ val valueInB = 100f
+ val valueInC = 200f
+
lateinit var layoutImpl: SceneTransitionLayoutImpl
rule.setContent {
SceneTransitionLayoutForTesting(
@@ -1103,7 +1116,9 @@
) {
// In scene A, Foo is aligned at the TopStart.
scene(SceneA) {
- Box(Modifier.fillMaxSize()) { Foo(sizeInA, Modifier.align(Alignment.TopStart)) }
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInA, valueInA, Modifier.align(Alignment.TopStart))
+ }
}
// In scene C, Foo is aligned at the BottomEnd, so it moves vertically when coming
@@ -1111,14 +1126,16 @@
// values and deltas are properly cleared once all transitions are done.
scene(SceneC) {
Box(Modifier.fillMaxSize()) {
- Foo(sizeInC, Modifier.align(Alignment.BottomEnd))
+ Foo(sizeInC, valueInC, Modifier.align(Alignment.BottomEnd))
}
}
// In scene B, Foo is aligned at the TopEnd, so it moves horizontally when coming
// from A.
scene(SceneB) {
- Box(Modifier.fillMaxSize()) { Foo(sizeInB, Modifier.align(Alignment.TopEnd)) }
+ Box(Modifier.fillMaxSize()) {
+ Foo(sizeInB, valueInB, Modifier.align(Alignment.TopEnd))
+ }
}
}
}
@@ -1134,6 +1151,10 @@
.assertSizeIsEqualTo(sizeInA)
.assertPositionInRootIsEqualTo(offsetInA.x, offsetInA.y)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInA)
+ assertThat(lastValues[SceneB]).isNull()
+ assertThat(lastValues[SceneC]).isNull()
+
// Current transition is A => B at 50%.
val aToBProgress = 0.5f
val aToB =
@@ -1145,12 +1166,17 @@
)
val offsetInAToB = lerp(offsetInA, offsetInB, aToBProgress)
val sizeInAToB = lerp(sizeInA, sizeInB, aToBProgress)
+ val valueInAToB = lerp(valueInA, valueInB, aToBProgress)
rule.runOnUiThread { state.startTransition(aToB, transitionKey = null) }
rule
.onNode(isElement(TestElements.Foo, SceneB))
.assertSizeIsEqualTo(sizeInAToB)
.assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneC]).isNull()
+
// Start B => C at 0%.
var bToCProgress by mutableFloatStateOf(0f)
var interruptionProgress by mutableFloatStateOf(1f)
@@ -1167,6 +1193,11 @@
// to the current transition offset and size.
val offsetInterruptionDelta = offsetInAToB - offsetInB
val sizeInterruptionDelta = sizeInAToB - sizeInB
+ val valueInterruptionDelta = valueInAToB - valueInB
+
+ assertThat(offsetInterruptionDelta).isNotEqualTo(DpOffset.Zero)
+ assertThat(sizeInterruptionDelta).isNotEqualTo(0.dp)
+ assertThat(valueInterruptionDelta).isNotEqualTo(0f)
// Interruption progress is at 100% and bToC is at 0%, so Foo should be at the same offset
// and size as right before the interruption.
@@ -1175,11 +1206,16 @@
.assertPositionInRootIsEqualTo(offsetInAToB.x, offsetInAToB.y)
.assertSizeIsEqualTo(sizeInAToB)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInAToB)
+ assertThat(lastValues[SceneC]).isWithin(0.001f).of(valueInAToB)
+
// Move the transition forward at 30% and set the interruption progress to 50%.
bToCProgress = 0.3f
interruptionProgress = 0.5f
val offsetInBToC = lerp(offsetInB, offsetInC, bToCProgress)
val sizeInBToC = lerp(sizeInB, sizeInC, bToCProgress)
+ val valueInBToC = lerp(valueInB, valueInC, bToCProgress)
val offsetInBToCWithInterruption =
offsetInBToC +
DpOffset(
@@ -1187,6 +1223,9 @@
offsetInterruptionDelta.y * interruptionProgress,
)
val sizeInBToCWithInterruption = sizeInBToC + sizeInterruptionDelta * interruptionProgress
+ val valueInBToCWithInterruption =
+ valueInBToC + valueInterruptionDelta * interruptionProgress
+
rule.waitForIdle()
rule
.onNode(isElement(TestElements.Foo, SceneB))
@@ -1196,6 +1235,10 @@
)
.assertSizeIsEqualTo(sizeInBToCWithInterruption)
+ assertThat(lastValues[SceneA]).isWithin(0.001f).of(valueInBToCWithInterruption)
+ assertThat(lastValues[SceneB]).isWithin(0.001f).of(valueInBToCWithInterruption)
+ assertThat(lastValues[SceneC]).isWithin(0.001f).of(valueInBToCWithInterruption)
+
// Finish the transition and interruption.
bToCProgress = 1f
interruptionProgress = 0f
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
index 3751a22..08532bd 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt
@@ -461,4 +461,30 @@
assertThat(exception).hasMessageThat().contains(Back.toString())
assertThat(exception).hasMessageThat().contains(SceneA.debugName)
}
+
+ @Test
+ fun sceneKeyInScope() {
+ val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(SceneA) }
+
+ var keyInA: SceneKey? = null
+ var keyInB: SceneKey? = null
+ var keyInC: SceneKey? = null
+ rule.setContent {
+ SceneTransitionLayout(state) {
+ scene(SceneA) { keyInA = sceneKey }
+ scene(SceneB) { keyInB = sceneKey }
+ scene(SceneC) { keyInC = sceneKey }
+ }
+ }
+
+ // Snap to B then C to compose these scenes at least once.
+ rule.runOnUiThread { state.snapToScene(SceneB) }
+ rule.waitForIdle()
+ rule.runOnUiThread { state.snapToScene(SceneC) }
+ rule.waitForIdle()
+
+ assertThat(keyInA).isEqualTo(SceneA)
+ assertThat(keyInB).isEqualTo(SceneB)
+ assertThat(keyInC).isEqualTo(SceneC)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
index 3a806a4..25ea2ee 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SwipeToSceneTest.kt
@@ -30,6 +30,7 @@
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.test.assertPositionInRootIsEqualTo
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performTouchInput
@@ -594,4 +595,43 @@
assertThat(transition).hasToScene(SceneB)
assertThat(transition).hasProgress(0.5f, tolerance = 0.01f)
}
+
+ @Test
+ fun overscrollScopeExtendsDensity() {
+ val swipeDistance = 100.dp
+ val state =
+ rule.runOnUiThread {
+ MutableSceneTransitionLayoutState(
+ SceneA,
+ transitions {
+ from(SceneA, to = SceneB) { distance = FixedDistance(swipeDistance) }
+
+ overscroll(SceneB, Orientation.Vertical) {
+ translate(TestElements.Foo, x = { 20.dp.toPx() }, y = { 30.dp.toPx() })
+ }
+ }
+ )
+ }
+ val layoutSize = 200.dp
+ var touchSlop = 0f
+ rule.setContent {
+ touchSlop = LocalViewConfiguration.current.touchSlop
+ SceneTransitionLayout(state, Modifier.size(layoutSize)) {
+ scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+ Box(Modifier.fillMaxSize())
+ }
+ scene(SceneB) { Box(Modifier.element(TestElements.Foo).fillMaxSize()) }
+ }
+ }
+
+ // Swipe down by twice the swipe distance so that we are at 100% overscrolling on scene B.
+ rule.onRoot().performTouchInput {
+ val middle = (layoutSize / 2).toPx()
+ down(Offset(middle, middle))
+ moveBy(Offset(0f, touchSlop + (swipeDistance * 2).toPx()), delayMillis = 1_000)
+ }
+
+ // Foo should be translated by (20dp, 30dp).
+ rule.onNode(isElement(TestElements.Foo)).assertPositionInRootIsEqualTo(20.dp, 30.dp)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
index a609be4..e6fa69d 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/Transition.kt
@@ -41,8 +41,10 @@
return object : TransitionState.Transition(from, to), TransitionState.HasOverscrollProperties {
override val currentScene: SceneKey
get() = current()
+
override val progress: Float
get() = progress()
+
override val progressVelocity: Float
get() = progressVelocity()
@@ -53,6 +55,8 @@
override val orientation: Orientation = orientation
override val overscrollScope: OverscrollScope =
object : OverscrollScope {
+ override val density: Float = 1f
+ override val fontScale: Float = 1f
override val absoluteDistance = 0f
}
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 89c5495..fb2b33d 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
@@ -34,6 +34,8 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.broadcastDispatcher
import com.android.systemui.communal.data.model.DisabledReason
+import com.android.systemui.communal.data.repository.CommunalSettingsRepositoryImpl.Companion.GLANCEABLE_HUB_BACKGROUND_SETTING
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
import com.android.systemui.flags.fakeFeatureFlagsClassic
@@ -43,6 +45,7 @@
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
@@ -216,6 +219,32 @@
)
}
+ @Test
+ fun backgroundType_defaultValue() =
+ testScope.runTest {
+ val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
+ assertThat(backgroundType).isEqualTo(CommunalBackgroundType.DEFAULT)
+ }
+
+ @Test
+ fun backgroundType_verifyAllValues() =
+ testScope.runTest {
+ val backgroundType by collectLastValue(underTest.getBackground(PRIMARY_USER))
+ for (type in CommunalBackgroundType.entries) {
+ kosmos.fakeSettings.putIntForUser(
+ GLANCEABLE_HUB_BACKGROUND_SETTING,
+ type.value,
+ PRIMARY_USER.id
+ )
+ assertWithMessage(
+ "Expected $type when $GLANCEABLE_HUB_BACKGROUND_SETTING is set to" +
+ " ${type.value} but was $backgroundType"
+ )
+ .that(backgroundType)
+ .isEqualTo(type)
+ }
+ }
+
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/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index a1bad39..9dcea82 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -37,6 +37,7 @@
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
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.domain.interactor.communalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -152,6 +153,7 @@
kosmos.keyguardInteractor,
kosmos.communalSceneInteractor,
kosmos.communalInteractor,
+ kosmos.communalSettingsInteractor,
kosmos.communalTutorialInteractor,
kosmos.shadeInteractor,
mediaHost,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 5068f68..78a1167 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -350,7 +350,7 @@
}
@Test
- fun quickAffordance_doNotSendUpdatesWhileShadeExpandingAndStillHidden() =
+ fun quickAffordance_updateOncePerShadeExpansion() =
testScope.runTest {
val shadeExpansion = MutableStateFlow(0f)
whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion)
@@ -365,9 +365,7 @@
shadeExpansion.value = i / 10f
}
- assertThat(collectedValue[0])
- .isInstanceOf(KeyguardQuickAffordanceModel.Hidden::class.java)
- assertThat(collectedValue.size).isEqualTo(initialSize)
+ assertThat(collectedValue.size).isEqualTo(initialSize + 1)
}
@Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
index b83b93b..10a4eb7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorTest.kt
@@ -50,6 +50,8 @@
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
+private const val builtInDeviceName = "This phone"
+
@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -71,6 +73,10 @@
addOverride(R.drawable.ic_media_speaker_device, testIcon)
addOverride(com.android.internal.R.drawable.ic_bt_hearing_aid, testIcon)
+
+ addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
}
}
}
@@ -90,7 +96,7 @@
assertThat(device).isInstanceOf(AudioOutputDevice.BuiltIn::class.java)
assertThat(device!!.icon).isEqualTo(testIcon)
- assertThat(device!!.name).isEqualTo("built_in")
+ assertThat(device!!.name).isEqualTo(builtInDeviceName)
}
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
new file mode 100644
index 0000000..8921a23
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorTest.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import android.graphics.drawable.TestStubDrawable
+import android.media.AudioManager
+import android.testing.TestableLooper
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.testKosmos
+import com.android.systemui.volume.data.repository.TestAudioDevicesFactory
+import com.android.systemui.volume.data.repository.audioRepository
+import com.android.systemui.volume.data.repository.audioSharingRepository
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.localMediaController
+import com.android.systemui.volume.localMediaRepository
+import com.android.systemui.volume.mediaControllerRepository
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+private const val builtInDeviceName = "This phone"
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+class MediaOutputComponentInteractorTest : SysuiTestCase() {
+
+ private val kosmos = testKosmos()
+
+ private lateinit var underTest: MediaOutputComponentInteractor
+
+ @Before
+ fun setUp() =
+ with(kosmos) {
+ audioRepository.setMode(AudioManager.MODE_NORMAL)
+ localMediaRepository.updateCurrentConnectedDevice(
+ TestMediaDevicesFactory.builtInMediaDevice(deviceIcon = testIcon)
+ )
+
+ with(context.orCreateTestableResources) {
+ addOverride(R.drawable.ic_smartphone, testIcon)
+
+ addOverride(R.string.media_transfer_this_device_name_tv, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name_tablet, builtInDeviceName)
+ addOverride(R.string.media_transfer_this_device_name, builtInDeviceName)
+ }
+
+ underTest = mediaOutputComponentInteractor
+ }
+
+ @Test
+ fun inCall_stateIs_Calling() =
+ with(kosmos) {
+ testScope.runTest {
+ with(audioRepository) {
+ setMode(AudioManager.MODE_IN_CALL)
+ setCommunicationDevice(TestAudioDevicesFactory.builtInDevice())
+ }
+
+ val model by collectLastValue(underTest.mediaOutputModel.filterData())
+ runCurrent()
+
+ assertThat(model)
+ .isEqualTo(
+ MediaOutputComponentModel.Calling(
+ AudioOutputDevice.BuiltIn(builtInDeviceName, testIcon),
+ false,
+ )
+ )
+ }
+ }
+
+ @Test
+ fun hasSession_stateIs_MediaSession() =
+ with(kosmos) {
+ testScope.runTest {
+ mediaControllerRepository.setActiveSessions(listOf(localMediaController))
+
+ val model by collectLastValue(underTest.mediaOutputModel.filterData())
+ runCurrent()
+
+ with(model as MediaOutputComponentModel.MediaSession) {
+ assertThat(session.appLabel).isEqualTo("local_media_controller_label")
+ assertThat(session.packageName).isEqualTo("local.test.pkg")
+ assertThat(session.canAdjustVolume).isTrue()
+ assertThat(device)
+ .isEqualTo(AudioOutputDevice.BuiltIn("built_in_media", testIcon))
+ assertThat(isInAudioSharing).isFalse()
+ }
+ }
+ }
+
+ @Test
+ fun noMediaOrCall_stateIs_Idle() =
+ with(kosmos) {
+ testScope.runTest {
+ audioSharingRepository.setInAudioSharing(true)
+
+ val model by collectLastValue(underTest.mediaOutputModel.filterData())
+ runCurrent()
+
+ assertThat(model)
+ .isEqualTo(
+ MediaOutputComponentModel.Idle(
+ AudioOutputDevice.BuiltIn("built_in_media", testIcon),
+ true,
+ )
+ )
+ }
+ }
+
+ private companion object {
+ val testIcon = TestStubDrawable()
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
index d497b4a..86a20dc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelTest.kt
@@ -31,14 +31,11 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.android.systemui.volume.data.repository.audioSharingRepository
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.android.systemui.volume.domain.interactor.audioOutputInteractor
import com.android.systemui.volume.localMediaController
import com.android.systemui.volume.localMediaRepository
import com.android.systemui.volume.mediaControllerRepository
-import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputActionsInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
@@ -66,10 +63,7 @@
applicationContext,
testScope.backgroundScope,
mediaOutputActionsInteractor,
- mediaDeviceSessionInteractor,
- audioOutputInteractor,
- audioModeInteractor,
- mediaOutputInteractor,
+ mediaOutputComponentInteractor,
uiEventLogger,
)
diff --git a/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml b/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml
new file mode 100644
index 0000000..82b222e7
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/ic_spatial_speaker.xml
@@ -0,0 +1,25 @@
+<!--
+ ~ 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.
+ -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="960"
+ android:viewportWidth="960">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M360,880q-134,0 -227,-93T40,560h80q0,100 70,170t170,70v80ZM360,740q-75,0 -127.5,-52.5T180,560h80q0,42 29,71t71,29v80ZM400,600q-33,0 -56.5,-23.5T320,520v-320q0,-33 23.5,-56.5T400,120h160q33,0 56.5,23.5T640,200v320q0,33 -23.5,56.5T560,600L400,600ZM400,520h160v-320L400,200v320ZM600,740v-80q42,0 71,-29t29,-71h80q0,75 -52.5,127.5T600,740ZM600,880v-80q100,0 170,-70t70,-170h80q0,134 -93,227T600,880ZM400,520h160,-160Z" />
+</vector>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 47e4b49..f688d4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -308,14 +308,14 @@
mStatusArea = mView.findViewById(R.id.keyguard_status_area);
mBgExecutor.execute(() -> {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK,
false, /* notifyForDescendants */
mDoubleLineClockObserver,
UserHandle.USER_ALL
);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED,
false, /* notifyForDescendants */
mShowWeatherObserver,
@@ -372,8 +372,8 @@
setClock(null);
mBgExecutor.execute(() -> {
- mSecureSettings.unregisterContentObserver(mDoubleLineClockObserver);
- mSecureSettings.unregisterContentObserver(mShowWeatherObserver);
+ mSecureSettings.unregisterContentObserverSync(mDoubleLineClockObserver);
+ mSecureSettings.unregisterContentObserverSync(mShowWeatherObserver);
});
mKeyguardUnlockAnimationController.removeKeyguardUnlockAnimationListener(
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
index ca24ccb..f2a68a8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationSettings.java
@@ -319,7 +319,7 @@
}
// Unregister observer before removing view
- mSecureSettings.unregisterContentObserver(mMagnificationCapabilityObserver);
+ mSecureSettings.unregisterContentObserverSync(mMagnificationCapabilityObserver);
mWindowManager.removeView(mSettingView);
mIsVisible = false;
if (resetPosition) {
@@ -380,7 +380,7 @@
mWindowManager.addView(mSettingView, mParams);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
mMagnificationCapabilityObserver,
UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index eb840f1..ffb5f3d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -240,26 +240,26 @@
}
void registerObserversAndCallbacks() {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS),
/* notifyForDescendants */ false, mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
if (!com.android.systemui.Flags.floatingMenuNarrowTargetContentObserver()) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ENABLED_ACCESSIBILITY_SERVICES),
/* notifyForDescendants */ false,
mMenuTargetFeaturesContentObserver,
UserHandle.USER_CURRENT);
}
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE),
/* notifyForDescendants */ false, mMenuSizeContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ACCESSIBILITY_FLOATING_MENU_FADE_ENABLED),
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(ACCESSIBILITY_FLOATING_MENU_OPACITY),
/* notifyForDescendants */ false, mMenuFadeOutContentObserver,
UserHandle.USER_CURRENT);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
index 91bc0c1..eaf541d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/fontscaling/FontScalingDialogDelegate.kt
@@ -29,12 +29,12 @@
import android.widget.TextView
import androidx.annotation.MainThread
import androidx.annotation.WorkerThread
-import com.android.systemui.res.R
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener
import com.android.systemui.common.ui.view.SeekBarWithIconButtonsView.OnSeekBarWithIconButtonsChangeListener.ControlUnitType
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -46,7 +46,9 @@
import kotlin.math.roundToInt
/** The Dialog that contains a seekbar for changing the font size. */
-class FontScalingDialogDelegate @Inject constructor(
+class FontScalingDialogDelegate
+@Inject
+constructor(
private val context: Context,
private val systemUIDialogFactory: SystemUIDialog.Factory,
private val layoutInflater: LayoutInflater,
@@ -84,9 +86,9 @@
dialog.setTitle(R.string.font_scaling_dialog_title)
dialog.setView(layoutInflater.inflate(R.layout.font_scaling_dialog, null))
dialog.setPositiveButton(
- R.string.quick_settings_done,
- /* onClick = */ null,
- /* dismissOnClick = */ true
+ R.string.quick_settings_done,
+ /* onClick = */ null,
+ /* dismissOnClick = */ true
)
}
@@ -142,7 +144,7 @@
}
)
doneButton.setOnClickListener { dialog.dismiss() }
- systemSettings.registerContentObserver(Settings.System.FONT_SCALE, fontSizeObserver)
+ systemSettings.registerContentObserverSync(Settings.System.FONT_SCALE, fontSizeObserver)
}
/**
@@ -165,7 +167,7 @@
override fun onStop(dialog: SystemUIDialog) {
cancelUpdateFontScaleRunnable?.run()
cancelUpdateFontScaleRunnable = null
- systemSettings.unregisterContentObserver(fontSizeObserver)
+ systemSettings.unregisterContentObserverSync(fontSizeObserver)
}
@MainThread
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
index 68c4a10..2970890 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/data/repository/FaceUserSettingsRepository.kt
@@ -75,7 +75,7 @@
) {
fun fetch(): Boolean = getIntForUser(key, if (defaultValue) 1 else 0, userId) > 0
- registerContentObserverForUser(
+ registerContentObserverForUserSync(
key,
false /* notifyForDescendants */,
object : ContentObserver(handler) {
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 88cb64c..1c47e50 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
@@ -30,6 +30,7 @@
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_INVALID_USER
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_USER_SETTING
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.flags.FeatureFlagsClassic
@@ -59,6 +60,9 @@
/** Keyguard widgets enabled state by Device Policy Manager for the specified user. */
fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean>
+
+ /** The type of background to use for the hub. Used to experiment with different backgrounds. */
+ fun getBackground(user: UserInfo): Flow<CommunalBackgroundType>
}
@SysUISingleton
@@ -126,6 +130,21 @@
.emitOnStart()
.map { devicePolicyManager.areKeyguardWidgetsAllowed(user.id) }
+ override fun getBackground(user: UserInfo): Flow<CommunalBackgroundType> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_BACKGROUND_SETTING))
+ .emitOnStart()
+ .map {
+ val intType =
+ secureSettings.getIntForUser(
+ GLANCEABLE_HUB_BACKGROUND_SETTING,
+ CommunalBackgroundType.DEFAULT.value,
+ user.id
+ )
+ CommunalBackgroundType.entries.find { type -> type.value == intType }
+ ?: CommunalBackgroundType.DEFAULT
+ }
+
private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
secureSettings
.observerFlow(userId = user.id, names = arrayOf(Settings.Secure.GLANCEABLE_HUB_ENABLED))
@@ -141,6 +160,7 @@
companion object {
const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
+ const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
private const val ENABLED_SETTING_DEFAULT = 1
}
}
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 3e5126a..f043d58 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.model.CommunalWidgetCategories
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.log.dagger.CommunalTableLog
@@ -30,6 +31,7 @@
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import java.util.concurrent.Executor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
@@ -38,6 +40,7 @@
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -47,6 +50,7 @@
@Inject
constructor(
@Background private val bgScope: CoroutineScope,
+ @Background private val bgDispatcher: CoroutineDispatcher,
@Background private val bgExecutor: Executor,
private val repository: CommunalSettingsRepository,
userInteractor: SelectedUserInteractor,
@@ -78,6 +82,12 @@
initialValue = CommunalWidgetCategories.defaultCategories
)
+ /** The type of background to use for the hub. Used to experiment with different backgrounds */
+ val communalBackground: Flow<CommunalBackgroundType> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.getBackground(user) }
+ .flowOn(bgDispatcher)
+
private val workProfileUserInfoCallbackFlow: Flow<UserInfo?> = conflatedCallbackFlow {
fun send(profiles: List<UserInfo>) {
trySend(profiles.find { it.isManagedProfile })
diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
new file mode 100644
index 0000000..8b816db
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalBackgroundType.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.communal.shared.model
+
+/** Models the types of background that can be shown on the hub. */
+enum class CommunalBackgroundType(val value: Int) {
+ DEFAULT(0),
+ STATIC_GRADIENT(1),
+ ANIMATED(2),
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index ce69ee8..3e00b04 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -21,8 +21,10 @@
import android.view.accessibility.AccessibilityNodeInfo
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.domain.interactor.CommunalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -70,6 +72,7 @@
keyguardInteractor: KeyguardInteractor,
communalSceneInteractor: CommunalSceneInteractor,
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
tutorialInteractor: CommunalTutorialInteractor,
private val shadeInteractor: ShadeInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
@@ -284,6 +287,10 @@
*/
val showGestureIndicator: Flow<Boolean> = not(keyguardInteractor.isDreaming)
+ /** The type of background to use for the hub. */
+ val communalBackground: Flow<CommunalBackgroundType> =
+ communalSettingsInteractor.communalBackground
+
companion object {
const val POPUP_AUTO_HIDE_TIMEOUT_MS = 12000L
}
@@ -291,5 +298,6 @@
sealed class PopupType {
object CtaTile : PopupType()
+
object CustomizeWidgetButton : PopupType()
}
diff --git a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
index 0bdc7f1..84807fb 100644
--- a/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
+++ b/packages/SystemUI/src/com/android/systemui/complication/ComplicationTypesUpdater.java
@@ -69,15 +69,15 @@
}
};
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.SCREENSAVER_COMPLICATIONS_ENABLED,
settingsObserver,
UserHandle.myUserId());
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.SCREENSAVER_HOME_CONTROLS_ENABLED,
settingsObserver,
UserHandle.myUserId());
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCKSCREEN_SHOW_CONTROLS,
settingsObserver,
UserHandle.myUserId());
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt b/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt
index 1bbdfcd..4dbb32d 100644
--- a/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt
+++ b/packages/SystemUI/src/com/android/systemui/dock/DockManagerExtensions.kt
@@ -19,18 +19,16 @@
import com.android.systemui.common.coroutine.ConflatedCallbackFlow
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
/**
- * Retrieves whether or not the device is docked according to DockManager. Emits a starting value of
- * isDocked.
+ * Retrieves whether or not the device is docked according to DockManager. Emits a starting value
+ * of isDocked.
*/
fun DockManager.retrieveIsDocked(): Flow<Boolean> =
ConflatedCallbackFlow.conflatedCallbackFlow {
- val callback = DockManager.DockEventListener { trySend(isDocked) }
- addListener(callback)
- trySend(isDocked)
+ val callback = DockManager.DockEventListener { trySend(isDocked) }
+ addListener(callback)
+ trySend(isDocked)
- awaitClose { removeListener(callback) }
- }
- .distinctUntilChanged()
+ awaitClose { removeListener(callback) }
+ }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index 3194942..7ae8409 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -426,7 +426,7 @@
}
if (!anyListening) {
- mSecureSettings.unregisterContentObserver(mSettingsObserver);
+ mSecureSettings.unregisterContentObserverSync(mSettingsObserver);
} else if (!mSettingRegistered) {
for (TriggerSensor s : mTriggerSensors) {
s.registerSettingsObserver(mSettingsObserver);
@@ -750,7 +750,7 @@
public void registerSettingsObserver(ContentObserver settingsObserver) {
if (mConfigured && !TextUtils.isEmpty(mSetting)) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSetting, mSettingsObserver, UserHandle.USER_ALL);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 49be03c..1e4fb4f 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -428,7 +428,7 @@
// get notified of phone state changes
mTelephonyListenerManager.addServiceStateListener(mPhoneStateListener);
- mGlobalSettings.registerContentObserver(
+ mGlobalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), true,
mAirplaneModeObserver);
mHasVibrator = vibrator.hasVibrator();
@@ -453,7 +453,7 @@
public void destroy() {
mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
mTelephonyListenerManager.removeServiceStateListener(mPhoneStateListener);
- mGlobalSettings.unregisterContentObserver(mAirplaneModeObserver);
+ mGlobalSettings.unregisterContentObserverSync(mAirplaneModeObserver);
mConfigurationController.removeCallback(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
index f488d3b..8ec460a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepository.kt
@@ -265,7 +265,7 @@
state: TransitionState
) {
if (updateTransitionId != transitionId) {
- Log.w(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
+ Log.wtf(TAG, "Attempting to update with old/invalid transitionId: $transitionId")
return
}
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 8ffa4bb..ccce3bf 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
@@ -105,35 +105,33 @@
}
return combine(
- quickAffordanceAlwaysVisible(position),
- keyguardInteractor.isDozing,
- if (SceneContainerFlag.isEnabled) {
- sceneInteractor
- .get()
- .transitionState
- .map {
- when (it) {
- is ObservableTransitionState.Idle ->
- it.currentScene == Scenes.Lockscreen
- is ObservableTransitionState.Transition ->
- it.fromScene == Scenes.Lockscreen ||
- it.toScene == Scenes.Lockscreen
- }
+ quickAffordanceAlwaysVisible(position),
+ keyguardInteractor.isDozing,
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor
+ .get()
+ .transitionState
+ .map {
+ when (it) {
+ is ObservableTransitionState.Idle ->
+ it.currentScene == Scenes.Lockscreen
+ is ObservableTransitionState.Transition ->
+ it.fromScene == Scenes.Lockscreen || it.toScene == Scenes.Lockscreen
}
- .distinctUntilChanged()
- } else {
- keyguardInteractor.isKeyguardShowing
- },
- shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
- biometricSettingsRepository.isCurrentUserInLockdown,
- ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
- if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
- affordance
- } else {
- KeyguardQuickAffordanceModel.Hidden
- }
+ }
+ .distinctUntilChanged()
+ } else {
+ keyguardInteractor.isKeyguardShowing
+ },
+ shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
+ biometricSettingsRepository.isCurrentUserInLockdown,
+ ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
+ if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
+ affordance
+ } else {
+ KeyguardQuickAffordanceModel.Hidden
}
- .distinctUntilChanged()
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
index 6d579f3..f5b82cf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBlueprint.kt
@@ -40,6 +40,7 @@
rebuildSections: List<KeyguardSection> = listOf(),
bindData: Boolean = true
) {
+ rebuildSections.forEach { it.onRebuildBegin() }
val prevSections = previousBlueprint?.sections ?: listOf()
val skipSections = sections.intersect(prevSections).subtract(rebuildSections)
prevSections.subtract(skipSections).forEach { it.removeViews(constraintLayout) }
@@ -49,6 +50,7 @@
it.bindData(constraintLayout)
}
}
+ rebuildSections.forEach { it.onRebuildEnd() }
}
/** Rebuilds views for the target sections, or all of them if unspecified. */
@@ -61,6 +63,7 @@
return
}
+ rebuildSections.forEach { it.onRebuildBegin() }
rebuildSections.forEach { it.removeViews(constraintLayout) }
rebuildSections.forEach {
it.addViews(constraintLayout)
@@ -68,6 +71,7 @@
it.bindData(constraintLayout)
}
}
+ rebuildSections.forEach { it.onRebuildEnd() }
}
fun applyConstraints(constraintSet: ConstraintSet) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
index 48a2146..473b08f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardSection.kt
@@ -33,6 +33,12 @@
/** Removes views and does any data binding destruction. */
abstract fun removeViews(constraintLayout: ConstraintLayout)
+ /* Notifies the section is being rebuilt */
+ open fun onRebuildBegin() {}
+
+ /* Notifies the secion that the rebuild is complete */
+ open fun onRebuildEnd() {}
+
/**
* Defines equality as same class.
*
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
index 807c322..23c2491 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardIndicationAreaBinder.kt
@@ -65,7 +65,7 @@
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
launch("$TAG#viewModel.alpha") {
// Do not independently apply alpha, as [KeyguardRootViewModel] should work
// for this and all its children
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
index 1cf009d..b9a79dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardQuickAffordanceViewBinder.kt
@@ -30,7 +30,6 @@
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle
-import com.android.app.tracing.coroutines.launch
import com.android.settingslib.Utils
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.view.LaunchableImageView
@@ -81,8 +80,8 @@
val configurationBasedDimensions = MutableStateFlow(loadFromResources(view))
val disposableHandle =
view.repeatWhenAttached {
- repeatOnLifecycle(Lifecycle.State.CREATED) {
- launch("$TAG#viewModel.collect") {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
viewModel.collect { buttonModel ->
updateButton(
view = button,
@@ -94,7 +93,7 @@
}
}
- launch("$TAG#updateButtonAlpha") {
+ launch {
updateButtonAlpha(
view = button,
viewModel = viewModel,
@@ -102,7 +101,7 @@
)
}
- launch("$TAG#configurationBasedDimensions") {
+ launch {
configurationBasedDimensions.collect { dimensions ->
button.updateLayoutParams<ViewGroup.LayoutParams> {
width = dimensions.buttonSizePx.width
@@ -324,6 +323,4 @@
private data class ConfigurationBasedDimensions(
val buttonSizePx: Size,
)
-
- private const val TAG = "KeyguardQuickAffordanceViewBinder"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
index 2d6690f..0435531 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/SmartspaceSection.kt
@@ -56,6 +56,14 @@
private var smartspaceVisibilityListener: OnGlobalLayoutListener? = null
private var pastVisibility: Int = -1
+ override fun onRebuildBegin() {
+ smartspaceController.suppressDisconnects = true
+ }
+
+ override fun onRebuildEnd() {
+ smartspaceController.suppressDisconnects = false
+ }
+
override fun addViews(constraintLayout: ConstraintLayout) {
if (!MigrateClocksToBlueprint.isEnabled) return
if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) return
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
index 244d842..c4383fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModel.kt
@@ -18,7 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
import androidx.annotation.VisibleForTesting
-import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -29,23 +28,19 @@
import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import javax.inject.Inject
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
-import kotlinx.coroutines.flow.stateIn
@OptIn(ExperimentalCoroutinesApi::class)
class KeyguardQuickAffordancesCombinedViewModel
@Inject
constructor(
- @Application applicationScope: CoroutineScope,
private val quickAffordanceInteractor: KeyguardQuickAffordanceInteractor,
private val keyguardInteractor: KeyguardInteractor,
shadeInteractor: ShadeInteractor,
@@ -89,20 +84,15 @@
/** The only time the expansion is important is while lockscreen is actively displayed */
private val shadeExpansionAlpha =
combine(
- showingLockscreen,
- shadeInteractor.anyExpansion,
- ) { showingLockscreen, expansion ->
- if (showingLockscreen) {
- 1 - expansion
- } else {
- 0f
- }
+ showingLockscreen,
+ shadeInteractor.anyExpansion,
+ ) { showingLockscreen, expansion ->
+ if (showingLockscreen) {
+ 1 - expansion
+ } else {
+ 0f
}
- .stateIn(
- scope = applicationScope,
- started = SharingStarted.Lazily,
- initialValue = 0f,
- )
+ }
/**
* ID of the slot that's currently selected in the preview that renders exclusively in the
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
index 7bacdeb..edead51 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaCarouselController.kt
@@ -397,7 +397,7 @@
listenForLockscreenSettingChanges(applicationScope)
// Notifies all active players about animation scale changes.
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
animationScaleObserver
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
index 6589038..601d563 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaHierarchyManager.kt
@@ -167,6 +167,7 @@
private var targetBounds: Rect = Rect()
private val mediaFrame
get() = mediaCarouselController.mediaFrame
+
private var statusbarState: Int = statusBarStateController.state
private var animator =
ValueAnimator.ofFloat(0.0f, 1.0f).apply {
@@ -600,7 +601,7 @@
}
}
}
- secureSettings.registerContentObserverForUser(
+ secureSettings.registerContentObserverForUserSync(
Settings.Secure.MEDIA_CONTROLS_LOCK_SCREEN,
settingsObserver,
UserHandle.USER_ALL
diff --git a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
index 2d460a0..765b45b 100644
--- a/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
+++ b/packages/SystemUI/src/com/android/systemui/qrcodescanner/controller/QRCodeScannerController.java
@@ -341,7 +341,7 @@
}
mQRCodeScannerPreferenceObserver.forEach((key, value) -> {
- mSecureSettings.unregisterContentObserver(value);
+ mSecureSettings.unregisterContentObserverSync(value);
});
// Reset cached values to default as we are no longer listening
@@ -418,7 +418,7 @@
});
}
});
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
mSecureSettings.getUriFor(LOCK_SCREEN_SHOW_QR_CODE_SCANNER), false,
mQRCodeScannerPreferenceObserver.get(userId), userId);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
index 2fafba1..e4bafcd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/AutoAddTracker.kt
@@ -52,7 +52,9 @@
*
* It also handles restore gracefully.
*/
-class AutoAddTracker @VisibleForTesting constructor(
+class AutoAddTracker
+@VisibleForTesting
+constructor(
private val secureSettings: SecureSettings,
private val broadcastDispatcher: BroadcastDispatcher,
private val qsHost: QSHost,
@@ -66,39 +68,43 @@
private val FILTER = IntentFilter(Intent.ACTION_SETTING_RESTORED)
}
- @GuardedBy("autoAdded")
- private val autoAdded = ArraySet<String>()
+ @GuardedBy("autoAdded") private val autoAdded = ArraySet<String>()
private var restoredTiles: Map<String, AutoTile>? = null
override val currentUserId: Int
get() = userId
- private val contentObserver = object : ContentObserver(mainHandler) {
- override fun onChange(
- selfChange: Boolean,
- uris: Collection<Uri>,
- flags: Int,
- _userId: Int
- ) {
- if (_userId != userId) {
- // Ignore changes outside of our user. We'll load the correct value on user change
- return
+ private val contentObserver =
+ object : ContentObserver(mainHandler) {
+ override fun onChange(
+ selfChange: Boolean,
+ uris: Collection<Uri>,
+ flags: Int,
+ _userId: Int
+ ) {
+ if (_userId != userId) {
+ // Ignore changes outside of our user. We'll load the correct value on user
+ // change
+ return
+ }
+ loadTiles()
}
- loadTiles()
}
- }
- private val restoreReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- if (intent.action != Intent.ACTION_SETTING_RESTORED) return
- processRestoreIntent(intent)
+ private val restoreReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action != Intent.ACTION_SETTING_RESTORED) return
+ processRestoreIntent(intent)
+ }
}
- }
private fun processRestoreIntent(intent: Intent) {
when (intent.getStringExtra(Intent.EXTRA_SETTING_NAME)) {
Settings.Secure.QS_TILES -> {
- restoredTiles = intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
+ restoredTiles =
+ intent
+ .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
?.split(DELIMITER)
?.mapIndexed(::AutoTile)
?.associateBy(AutoTile::tileType)
@@ -109,13 +115,11 @@
}
Settings.Secure.QS_AUTO_ADDED_TILES -> {
restoredTiles?.let { restoredTiles ->
- val restoredAutoAdded = intent
- .getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)
- ?.split(DELIMITER)
+ val restoredAutoAdded =
+ intent.getStringExtra(Intent.EXTRA_SETTING_NEW_VALUE)?.split(DELIMITER)
?: emptyList()
- val autoAddedBeforeRestore = intent
- .getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)
- ?.split(DELIMITER)
+ val autoAddedBeforeRestore =
+ intent.getStringExtra(Intent.EXTRA_SETTING_PREVIOUS_VALUE)?.split(DELIMITER)
?: emptyList()
val tilesToRemove = restoredAutoAdded.filter { it !in restoredTiles }
@@ -123,50 +127,51 @@
Log.d(TAG, "Removing tiles: $tilesToRemove")
qsHost.removeTiles(tilesToRemove)
}
- val tiles = synchronized(autoAdded) {
- autoAdded.clear()
- autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
- getTilesFromListLocked()
- }
+ val tiles =
+ synchronized(autoAdded) {
+ autoAdded.clear()
+ autoAdded.addAll(restoredAutoAdded + autoAddedBeforeRestore)
+ getTilesFromListLocked()
+ }
saveTiles(tiles)
- } ?: run {
- Log.w(TAG, "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
- "${Settings.Secure.QS_TILES} for user $userId")
}
+ ?: run {
+ Log.w(
+ TAG,
+ "${Settings.Secure.QS_AUTO_ADDED_TILES} restored before " +
+ "${Settings.Secure.QS_TILES} for user $userId"
+ )
+ }
}
else -> {} // Do nothing for other Settings
}
}
- /**
- * Init method must be called after construction to start listening
- */
+ /** Init method must be called after construction to start listening */
fun initialize() {
dumpManager.registerDumpable(TAG, this)
loadTiles()
- secureSettings.registerContentObserverForUser(
- secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
- contentObserver,
- UserHandle.USER_ALL
+ secureSettings.registerContentObserverForUserSync(
+ secureSettings.getUriFor(Settings.Secure.QS_AUTO_ADDED_TILES),
+ contentObserver,
+ UserHandle.USER_ALL
)
registerBroadcastReceiver()
}
- /**
- * Unregister listeners, receivers and observers
- */
+ /** Unregister listeners, receivers and observers */
fun destroy() {
dumpManager.unregisterDumpable(TAG)
- secureSettings.unregisterContentObserver(contentObserver)
+ secureSettings.unregisterContentObserverSync(contentObserver)
unregisterBroadcastReceiver()
}
private fun registerBroadcastReceiver() {
broadcastDispatcher.registerReceiver(
- restoreReceiver,
- FILTER,
- backgroundExecutor,
- UserHandle.of(userId)
+ restoreReceiver,
+ FILTER,
+ backgroundExecutor,
+ UserHandle.of(userId)
)
}
@@ -186,13 +191,9 @@
fun getRestoredTilePosition(tile: String): Int =
restoredTiles?.get(tile)?.index ?: QSHost.POSITION_AT_END
- /**
- * Returns `true` if the tile has been auto-added before
- */
+ /** Returns `true` if the tile has been auto-added before */
fun isAdded(tile: String): Boolean {
- return synchronized(autoAdded) {
- tile in autoAdded
- }
+ return synchronized(autoAdded) { tile in autoAdded }
}
/**
@@ -201,13 +202,14 @@
* From here on, [isAdded] will return true for that tile.
*/
fun setTileAdded(tile: String) {
- val tiles = synchronized(autoAdded) {
- if (autoAdded.add(tile)) {
- getTilesFromListLocked()
- } else {
- null
+ val tiles =
+ synchronized(autoAdded) {
+ if (autoAdded.add(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
+ }
}
- }
tiles?.let { saveTiles(it) }
}
@@ -217,13 +219,14 @@
* This allows for this tile to be auto-added again in the future.
*/
fun setTileRemoved(tile: String) {
- val tiles = synchronized(autoAdded) {
- if (autoAdded.remove(tile)) {
- getTilesFromListLocked()
- } else {
- null
+ val tiles =
+ synchronized(autoAdded) {
+ if (autoAdded.remove(tile)) {
+ getTilesFromListLocked()
+ } else {
+ null
+ }
}
- }
tiles?.let { saveTiles(it) }
}
@@ -233,12 +236,12 @@
private fun saveTiles(tiles: String) {
secureSettings.putStringForUser(
- Settings.Secure.QS_AUTO_ADDED_TILES,
- tiles,
- /* tag */ null,
- /* makeDefault */ false,
- userId,
- /* overrideableByRestore */ true
+ Settings.Secure.QS_AUTO_ADDED_TILES,
+ tiles,
+ /* tag */ null,
+ /* makeDefault */ false,
+ userId,
+ /* overrideableByRestore */ true
)
}
@@ -261,7 +264,9 @@
}
@SysUISingleton
- class Builder @Inject constructor(
+ class Builder
+ @Inject
+ constructor(
private val secureSettings: SecureSettings,
private val broadcastDispatcher: BroadcastDispatcher,
private val qsHost: QSHost,
@@ -278,16 +283,16 @@
fun build(): AutoAddTracker {
return AutoAddTracker(
- secureSettings,
- broadcastDispatcher,
- qsHost,
- dumpManager,
- handler,
- executor,
- userId
+ secureSettings,
+ broadcastDispatcher,
+ qsHost,
+ dumpManager,
+ handler,
+ executor,
+ userId
)
}
}
private data class AutoTile(val index: Int, val tileType: String)
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
index 4fc6609..846d63f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/ReduceBrightColorsControllerImpl.java
@@ -77,8 +77,8 @@
public void onUserChanged(int newUser, Context userContext) {
synchronized (mListeners) {
if (mListeners.size() > 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
false, mContentObserver, newUser);
}
@@ -94,7 +94,7 @@
if (!mListeners.contains(listener)) {
mListeners.add(listener);
if (mListeners.size() == 1) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.REDUCE_BRIGHT_COLORS_ACTIVATED,
false, mContentObserver, mUserTracker.getUserId());
}
@@ -106,7 +106,7 @@
public void removeCallback(@androidx.annotation.NonNull Listener listener) {
synchronized (mListeners) {
if (mListeners.remove(listener) && mListeners.size() == 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
index eb11568..6092348 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/SettingObserver.java
@@ -74,10 +74,10 @@
mListening = listening;
if (listening) {
mObservedValue = getValueFromProvider();
- mSettingsProxy.registerContentObserver(
+ mSettingsProxy.registerContentObserverSync(
mSettingsProxy.getUriFor(mSettingName), false, this);
} else {
- mSettingsProxy.unregisterContentObserver(this);
+ mSettingsProxy.unregisterContentObserverSync(this);
mObservedValue = mDefaultValue;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
index 539c2d6..1b34c33 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/UserSettingObserver.java
@@ -76,10 +76,10 @@
mListening = listening;
if (listening) {
mObservedValue = getValueFromProvider();
- mSettingsProxy.registerContentObserverForUser(
+ mSettingsProxy.registerContentObserverForUserSync(
mSettingsProxy.getUriFor(mSettingName), false, this, mUserId);
} else {
- mSettingsProxy.unregisterContentObserver(this);
+ mSettingsProxy.unregisterContentObserverSync(this);
mObservedValue = mDefaultValue;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
index d452241..9fcb0db 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserAutoAddRepository.kt
@@ -44,9 +44,8 @@
@Background private val bgDispatcher: CoroutineDispatcher,
) {
- private val changeEvents = MutableSharedFlow<ChangeAction>(
- extraBufferCapacity = CHANGES_BUFFER_SIZE
- )
+ private val changeEvents =
+ MutableSharedFlow<ChangeAction>(extraBufferCapacity = CHANGES_BUFFER_SIZE)
private lateinit var _autoAdded: StateFlow<Set<TileSpec>>
@@ -85,8 +84,8 @@
trySend(Unit)
}
}
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
- awaitClose { secureSettings.unregisterContentObserver(observer) }
+ secureSettings.registerContentObserverForUserSync(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserverSync(observer) }
}
.map { load() }
.flowOn(bgDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
index aca8733..1f9570a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/UserTileSpecRepository.kt
@@ -98,8 +98,8 @@
trySend(Unit)
}
}
- secureSettings.registerContentObserverForUser(SETTING, observer, userId)
- awaitClose { secureSettings.unregisterContentObserver(observer) }
+ secureSettings.registerContentObserverForUserSync(SETTING, observer, userId)
+ awaitClose { secureSettings.unregisterContentObserverSync(observer) }
}
.map { loadTilesFromSettings(userId) }
.flowOn(backgroundDispatcher)
diff --git a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
index 3c0aa38..09fd7df 100644
--- a/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/retail/data/repository/RetailModeRepository.kt
@@ -66,9 +66,9 @@
}
}
- globalSettings.registerContentObserver(RETAIL_MODE_SETTING, observer)
+ globalSettings.registerContentObserverSync(RETAIL_MODE_SETTING, observer)
- awaitClose { globalSettings.unregisterContentObserver(observer) }
+ awaitClose { globalSettings.unregisterContentObserverSync(observer) }
}
.onStart { emit(Unit) }
.map { globalSettings.getInt(RETAIL_MODE_SETTING, 0) != 0 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index 37f2a21..49810762 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -137,14 +137,14 @@
public void startObserving() {
if (!mObserving) {
mObserving = true;
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
BRIGHTNESS_MODE_URI,
false, this, UserHandle.USER_ALL);
}
}
public void stopObserving() {
- mSecureSettings.unregisterContentObserver(this);
+ mSecureSettings.unregisterContentObserverSync(this);
mObserving = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
index 95768e5..2dfc920 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ImmersiveModeConfirmation.java
@@ -318,10 +318,10 @@
};
// Register to listen for changes in Settings.Secure settings.
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS, mContentObserver,
UserHandle.USER_CURRENT);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.USER_SETUP_COMPLETE, mContentObserver,
UserHandle.USER_CURRENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 455c964..2e87a5b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -143,6 +143,12 @@
private var managedUserHandle: UserHandle? = null
private var mSplitShadeEnabled = false
+ var suppressDisconnects = false
+ set(value) {
+ field = value
+ disconnect()
+ }
+
// TODO(b/202758428): refactor so that we can test color updates via region samping, similar to
// how we test color updates when theme changes (See testThemeChangeUpdatesTextColor).
@@ -522,6 +528,7 @@
*/
fun disconnect() {
if (!smartspaceViews.isEmpty()) return
+ if (suppressDisconnects) return
execution.assertIsMainThread()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
index 4c2ef83..4c82bc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProvider.kt
@@ -37,11 +37,13 @@
import javax.inject.Inject
/**
- * A class which provides an adjustment object to the preparation coordinator which is uses
- * to ensure that notifications are reinflated when ranking-derived information changes.
+ * A class which provides an adjustment object to the preparation coordinator which is uses to
+ * ensure that notifications are reinflated when ranking-derived information changes.
*/
@SysUISingleton
-class NotifUiAdjustmentProvider @Inject constructor(
+class NotifUiAdjustmentProvider
+@Inject
+constructor(
@Main private val handler: Handler,
private val secureSettings: SecureSettings,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -53,14 +55,13 @@
private val dirtyListeners = ListenerSet<Runnable>()
private var isSnoozeSettingsEnabled = false
- /**
- * Update the snooze enabled value on user switch
- */
- private val userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- updateSnoozeEnabled()
+ /** Update the snooze enabled value on user switch */
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ updateSnoozeEnabled()
+ }
}
- }
init {
userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
@@ -75,7 +76,7 @@
)
}
updateSnoozeEnabled()
- secureSettings.registerContentObserverForUser(
+ secureSettings.registerContentObserverForUserSync(
SHOW_NOTIFICATION_SNOOZE,
settingsObserver,
UserHandle.USER_ALL
@@ -93,7 +94,7 @@
onSensitiveStateChangedListener
)
}
- secureSettings.unregisterContentObserver(settingsObserver)
+ secureSettings.unregisterContentObserverSync(settingsObserver)
}
}
@@ -104,12 +105,13 @@
private val onSensitiveStateChangedListener = Runnable { dirtyListeners.forEach(Runnable::run) }
- private val settingsObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean) {
- updateSnoozeEnabled()
- dirtyListeners.forEach(Runnable::run)
+ private val settingsObserver =
+ object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean) {
+ updateSnoozeEnabled()
+ dirtyListeners.forEach(Runnable::run)
+ }
}
- }
private fun updateSnoozeEnabled() {
isSnoozeSettingsEnabled =
@@ -126,22 +128,23 @@
}
/**
- * Returns a adjustment object for the given entry. This can be compared to a previous instance
+ * Returns a adjustment object for the given entry. This can be compared to a previous instance
* from the same notification using [NotifUiAdjustment.needReinflate] to determine if it should
* be reinflated.
*/
- fun calculateAdjustment(entry: NotificationEntry) = NotifUiAdjustment(
- key = entry.key,
- smartActions = entry.ranking.smartActions,
- smartReplies = entry.ranking.smartReplies,
- isConversation = entry.ranking.isConversation,
- isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
- isMinimized = isEntryMinimized(entry),
- needsRedaction =
- lockscreenUserManager.needsRedaction(entry) ||
- (screenshareNotificationHiding() &&
- sensitiveNotifProtectionController.shouldProtectNotification(entry)),
- isChildInGroup = entry.hasEverBeenGroupChild(),
- isGroupSummary = entry.hasEverBeenGroupSummary(),
- )
+ fun calculateAdjustment(entry: NotificationEntry) =
+ NotifUiAdjustment(
+ key = entry.key,
+ smartActions = entry.ranking.smartActions,
+ smartReplies = entry.ranking.smartReplies,
+ isConversation = entry.ranking.isConversation,
+ isSnoozeEnabled = isSnoozeSettingsEnabled && !entry.isCanceled,
+ isMinimized = isEntryMinimized(entry),
+ needsRedaction =
+ lockscreenUserManager.needsRedaction(entry) ||
+ (screenshareNotificationHiding() &&
+ sensitiveNotifProtectionController.shouldProtectNotification(entry)),
+ isChildInGroup = entry.hasEverBeenGroupChild(),
+ isGroupSummary = entry.hasEverBeenGroupSummary(),
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
index 42a5bdf..f84b5f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/CommonVisualInterruptionSuppressors.kt
@@ -33,8 +33,8 @@
import android.provider.Settings
import android.provider.Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED
import android.provider.Settings.Global.HEADS_UP_OFF
+import com.android.internal.logging.UiEvent
import com.android.internal.logging.UiEventLogger
-import com.android.internal.logging.UiEvent;
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
@@ -86,7 +86,7 @@
}
}
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
globalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
/* notifyForDescendants = */ true,
observer
@@ -94,7 +94,7 @@
// QQQ: Do we need to register for SETTING_HEADS_UP_TICKER? It seems unused.
- observer.onChange(/* selfChange = */ true)
+ observer.onChange(/* selfChange= */ true)
}
}
@@ -243,6 +243,7 @@
override fun shouldSuppress(entry: NotificationEntry) =
keyguardNotificationVisibilityProvider.shouldHideNotification(entry)
}
+
class AvalancheSuppressor(
private val avalancheProvider: AvalancheProvider,
private val systemClock: SystemClock,
@@ -268,38 +269,33 @@
SUPPRESS
}
- enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
- @UiEvent(doc = "An avalanche event occurred but this notification was suppressed by a " +
- "non-avalanche suppressor.")
- START(1802),
-
- @UiEvent(doc = "HUN was suppressed in avalanche.")
- SUPPRESS(1803),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
- ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
- ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a call.")
- ALLOW_CALLSTYLE(1806),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
- ALLOW_CATEGORY_REMINDER(1807),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
- ALLOW_CATEGORY_EVENT(1808),
-
- @UiEvent(doc = "HUN allowed during avalanche because it has a full screen intent and " +
- "the full screen intent permission is granted.")
- ALLOW_FSI_WITH_PERMISSION_ON(1809),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
- ALLOW_COLORIZED(1810),
-
- @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
- ALLOW_EMERGENCY(1811);
+ enum class AvalancheEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(
+ doc =
+ "An avalanche event occurred but this notification was suppressed by a " +
+ "non-avalanche suppressor."
+ )
+ START(1802),
+ @UiEvent(doc = "HUN was suppressed in avalanche.") SUPPRESS(1803),
+ @UiEvent(doc = "HUN allowed during avalanche because it is high priority.")
+ ALLOW_CONVERSATION_AFTER_AVALANCHE(1804),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a high priority conversation.")
+ ALLOW_HIGH_PRIORITY_CONVERSATION_ANY_TIME(1805),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a call.") ALLOW_CALLSTYLE(1806),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
+ ALLOW_CATEGORY_REMINDER(1807),
+ @UiEvent(doc = "HUN allowed during avalanche because it is a calendar notification.")
+ ALLOW_CATEGORY_EVENT(1808),
+ @UiEvent(
+ doc =
+ "HUN allowed during avalanche because it has a full screen intent and " +
+ "the full screen intent permission is granted."
+ )
+ ALLOW_FSI_WITH_PERMISSION_ON(1809),
+ @UiEvent(doc = "HUN allowed during avalanche because it is colorized.")
+ ALLOW_COLORIZED(1810),
+ @UiEvent(doc = "HUN allowed during avalanche because it is an emergency notification.")
+ ALLOW_EMERGENCY(1811);
override fun getId(): Int {
return id
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index 332ece4..c7548aa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -14,7 +14,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.NotificationLockscreenUserManager
@@ -41,8 +40,8 @@
/** Determines if notifications should be visible based on the state of the keyguard. */
interface KeyguardNotificationVisibilityProvider {
/**
- * Determines if the given notification should be hidden based on the current keyguard state.
- * If a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
+ * Determines if the given notification should be hidden based on the current keyguard state. If
+ * a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
* method may no longer be valid and should be re-queried.
*/
fun shouldHideNotification(entry: NotificationEntry): Boolean
@@ -61,8 +60,9 @@
@Module
interface KeyguardNotificationVisibilityProviderImplModule {
@Binds
- fun bindImpl(impl: KeyguardNotificationVisibilityProviderImpl):
- KeyguardNotificationVisibilityProvider
+ fun bindImpl(
+ impl: KeyguardNotificationVisibilityProviderImpl
+ ): KeyguardNotificationVisibilityProvider
@Binds
@IntoMap
@@ -71,7 +71,9 @@
}
@SysUISingleton
-class KeyguardNotificationVisibilityProviderImpl @Inject constructor(
+class KeyguardNotificationVisibilityProviderImpl
+@Inject
+constructor(
@Main private val handler: Handler,
private val keyguardStateController: KeyguardStateController,
private val lockscreenUserManager: NotificationLockscreenUserManager,
@@ -84,76 +86,88 @@
private val featureFlags: FeatureFlagsClassic
) : CoreStartable, KeyguardNotificationVisibilityProvider {
private val showSilentNotifsUri =
- secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
+ secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
private val onStateChangedListeners = ListenerSet<Consumer<String>>()
private var hideSilentNotificationsOnLockscreen: Boolean = false
- private val userTrackerCallback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- readShowSilentNotificationSetting()
- if (isLockedOrLocking) {
- // maybe public mode changed
- notifyStateChanged("onUserSwitched")
+ private val userTrackerCallback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ readShowSilentNotificationSetting()
+ if (isLockedOrLocking) {
+ // maybe public mode changed
+ notifyStateChanged("onUserSwitched")
+ }
}
}
- }
override fun start() {
readShowSilentNotificationSetting()
- keyguardStateController.addCallback(object : KeyguardStateController.Callback {
- override fun onUnlockedChanged() {
- notifyStateChanged("onUnlockedChanged")
- }
+ keyguardStateController.addCallback(
+ object : KeyguardStateController.Callback {
+ override fun onUnlockedChanged() {
+ notifyStateChanged("onUnlockedChanged")
+ }
- override fun onKeyguardShowingChanged() {
- notifyStateChanged("onKeyguardShowingChanged")
+ override fun onKeyguardShowingChanged() {
+ notifyStateChanged("onKeyguardShowingChanged")
+ }
}
- })
- keyguardUpdateMonitor.registerCallback(object : KeyguardUpdateMonitorCallback() {
- override fun onStrongAuthStateChanged(userId: Int) {
- notifyStateChanged("onStrongAuthStateChanged")
+ )
+ keyguardUpdateMonitor.registerCallback(
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onStrongAuthStateChanged(userId: Int) {
+ notifyStateChanged("onStrongAuthStateChanged")
+ }
}
- })
+ )
// register lockscreen settings changed callbacks:
- val settingsObserver: ContentObserver = object : ContentObserver(handler) {
- override fun onChange(selfChange: Boolean, uri: Uri?) {
- if (uri == showSilentNotifsUri) {
- readShowSilentNotificationSetting()
- }
- if (isLockedOrLocking) {
- notifyStateChanged("Settings $uri changed")
+ val settingsObserver: ContentObserver =
+ object : ContentObserver(handler) {
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ if (uri == showSilentNotifsUri) {
+ readShowSilentNotificationSetting()
+ }
+ if (isLockedOrLocking) {
+ notifyStateChanged("Settings $uri changed")
+ }
}
}
- }
- secureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
- settingsObserver,
- UserHandle.USER_ALL)
+ secureSettings.registerContentObserverForUserSync(
+ Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
- secureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
- true,
- settingsObserver,
- UserHandle.USER_ALL)
+ secureSettings.registerContentObserverForUserSync(
+ Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
+ true,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
- globalSettings.registerContentObserver(Settings.Global.ZEN_MODE, settingsObserver)
+ globalSettings.registerContentObserverSync(Settings.Global.ZEN_MODE, settingsObserver)
- secureSettings.registerContentObserverForUser(
- Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
- settingsObserver,
- UserHandle.USER_ALL)
+ secureSettings.registerContentObserverForUserSync(
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+ settingsObserver,
+ UserHandle.USER_ALL
+ )
// register (maybe) public mode changed callbacks:
- statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
- override fun onStateChanged(newState: Int) {
- notifyStateChanged("onStatusBarStateChanged")
+ statusBarStateController.addCallback(
+ object : StatusBarStateController.StateListener {
+ override fun onStateChanged(newState: Int) {
+ notifyStateChanged("onStatusBarStateChanged")
+ }
+
+ override fun onUpcomingStateChanged(state: Int) {
+ notifyStateChanged("onStatusBarUpcomingStateChanged")
+ }
}
- override fun onUpcomingStateChanged(state: Int) {
- notifyStateChanged("onStatusBarUpcomingStateChanged")
- }
- })
+ )
userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
}
@@ -169,45 +183,48 @@
onStateChangedListeners.forEach { it.accept(reason) }
}
- override fun shouldHideNotification(entry: NotificationEntry): Boolean = when {
- // Keyguard state doesn't matter if the keyguard is not showing.
- !isLockedOrLocking -> false
- // Notifications not allowed on the lockscreen, always hide.
- !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
- // User settings do not allow this notification on the lockscreen, so hide it.
- userSettingsDisallowNotification(entry) -> true
- // Entry is explicitly marked SECRET, so hide it.
- entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
- // if entry is silent, apply custom logic to see if should hide
- shouldHideIfEntrySilent(entry) -> true
- else -> false
- }
+ override fun shouldHideNotification(entry: NotificationEntry): Boolean =
+ when {
+ // Keyguard state doesn't matter if the keyguard is not showing.
+ !isLockedOrLocking -> false
+ // Notifications not allowed on the lockscreen, always hide.
+ !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
+ // User settings do not allow this notification on the lockscreen, so hide it.
+ userSettingsDisallowNotification(entry) -> true
+ // Entry is explicitly marked SECRET, so hide it.
+ entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
+ // if entry is silent, apply custom logic to see if should hide
+ shouldHideIfEntrySilent(entry) -> true
+ else -> false
+ }
- private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean = when {
- // Show if explicitly high priority (not hidden)
- highPriorityProvider.isExplicitlyHighPriority(entry) -> false
- // Ambient notifications are hidden always from lock screen
- entry.representativeEntry?.isAmbient == true -> true
- // [Now notification is silent]
- // Hide regardless of parent priority if user wants silent notifs hidden
- hideSilentNotificationsOnLockscreen -> true
- // Parent priority is high enough to be shown on the lockscreen, do not hide.
- entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
- // Show when silent notifications are allowed on lockscreen
- else -> false
- }
+ private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean =
+ when {
+ // Show if explicitly high priority (not hidden)
+ highPriorityProvider.isExplicitlyHighPriority(entry) -> false
+ // Ambient notifications are hidden always from lock screen
+ entry.representativeEntry?.isAmbient == true -> true
+ // [Now notification is silent]
+ // Hide regardless of parent priority if user wants silent notifs hidden
+ hideSilentNotificationsOnLockscreen -> true
+ // Parent priority is high enough to be shown on the lockscreen, do not hide.
+ entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
+ // Show when silent notifications are allowed on lockscreen
+ else -> false
+ }
private fun userSettingsDisallowNotification(entry: NotificationEntry): Boolean {
- fun disallowForUser(user: Int) = when {
- // user is in lockdown, always disallow
- keyguardUpdateMonitor.isUserInLockdown(user) -> true
- // device isn't public, no need to check public-related settings, so allow
- !lockscreenUserManager.isLockscreenPublicMode(user) -> false
- // entry is meant to be secret on the lockscreen, disallow
- isRankingVisibilitySecret(entry) -> true
- // disallow if user disallows notifications in public
- else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
- }
+ fun disallowForUser(user: Int) =
+ when {
+ // user is in lockdown, always disallow
+ keyguardUpdateMonitor.isUserInLockdown(user) -> true
+ // device isn't public, no need to check public-related settings, so allow
+ !lockscreenUserManager.isLockscreenPublicMode(user) -> false
+ // entry is meant to be secret on the lockscreen, disallow
+ isRankingVisibilitySecret(entry) -> true
+ // disallow if user disallows notifications in public
+ else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
+ }
val currentUser = lockscreenUserManager.currentUserId
val notifUser = entry.sbn.user.identifier
return when {
@@ -222,28 +239,35 @@
// ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
// info, and NotificationLockscreenUserManagerImpl is already listening for updates
// to those
- return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
- VISIBILITY_SECRET
+ return entry.ranking.channel != null &&
+ entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET
}
- override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
- println("isLockedOrLocking", isLockedOrLocking)
- withIncreasedIndent {
- println("keyguardStateController.isShowing", keyguardStateController.isShowing)
- println("statusBarStateController.currentOrUpcomingState",
- statusBarStateController.currentOrUpcomingState)
+ override fun dump(pw: PrintWriter, args: Array<out String>) =
+ pw.asIndenting().run {
+ println("isLockedOrLocking", isLockedOrLocking)
+ withIncreasedIndent {
+ println("keyguardStateController.isShowing", keyguardStateController.isShowing)
+ println(
+ "statusBarStateController.currentOrUpcomingState",
+ statusBarStateController.currentOrUpcomingState
+ )
+ }
+ println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
}
- println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
- }
- private val isLockedOrLocking get() =
- keyguardStateController.isShowing ||
+ private val isLockedOrLocking
+ get() =
+ keyguardStateController.isShowing ||
statusBarStateController.currentOrUpcomingState == StatusBarState.KEYGUARD
private fun readShowSilentNotificationSetting() {
val showSilentNotifs =
- secureSettings.getBoolForUser(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
- false, UserHandle.USER_CURRENT)
+ secureSettings.getBoolForUser(
+ Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
+ false,
+ UserHandle.USER_CURRENT
+ )
hideSilentNotificationsOnLockscreen = !showSilentNotifs
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
index 74925c8..fea360d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptStateProviderImpl.java
@@ -171,11 +171,11 @@
};
if (ENABLE_HEADS_UP) {
- mGlobalSettings.registerContentObserver(
+ mGlobalSettings.registerContentObserverSync(
mGlobalSettings.getUriFor(HEADS_UP_NOTIFICATIONS_ENABLED),
true,
headsUpObserver);
- mGlobalSettings.registerContentObserver(
+ mGlobalSettings.registerContentObserverSync(
mGlobalSettings.getUriFor(SETTING_HEADS_UP_TICKER), true,
headsUpObserver);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
index a17c066..30dbfed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationSettingsController.java
@@ -92,9 +92,9 @@
synchronized (mListeners) {
if (mListeners.size() > 0) {
- mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
for (Uri uri : mListeners.keySet()) {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
uri, false, mContentObserver, newUser);
}
}
@@ -131,7 +131,7 @@
mListeners.put(uri, currentListeners);
if (currentListeners.size() == 1) {
mBackgroundHandler.post(() -> {
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
uri, false, mContentObserver, mUserTracker.getUserId());
});
}
@@ -159,7 +159,7 @@
if (mListeners.size() == 0) {
mBackgroundHandler.post(() -> {
- mSecureSettings.unregisterContentObserver(mContentObserver);
+ mSecureSettings.unregisterContentObserverSync(mContentObserver);
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 6d4301f..e44edcb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -398,7 +398,7 @@
mSystemIconsContainer.setOnHoverListener(hoverListener);
mView.setOnApplyWindowInsetsListener(
(view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
mVolumeSettingObserver,
@@ -416,7 +416,7 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mDisableStateTracker.stopTracking(mCommandQueue);
- mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
+ mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
if (mTintedIconManager != null) {
mStatusBarIconController.removeIconGroup(mTintedIconManager);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 479aef1..c53558e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -23,9 +23,9 @@
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.MigrateClocksToBlueprint
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
+import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.NotificationShadeWindowController
@@ -162,7 +162,7 @@
this.centralSurfaces = centralSurfaces
updateAnimatorDurationScale()
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE),
/* notify for descendants */ false,
animatorDurationScaleObserver
@@ -376,8 +376,9 @@
// We currently draw both the light reveal scrim, and the AOD UI, in the shade. If it's
// already expanded and showing notifications/QS, the animation looks really messy. For now,
// disable it if the notification panel is expanded.
- if ((!this::centralSurfaces.isInitialized ||
- panelExpansionInteractorLazy.get().isPanelExpanded) &&
+ if (
+ (!this::centralSurfaces.isInitialized ||
+ panelExpansionInteractorLazy.get().isPanelExpanded) &&
// Status bar might be expanded because we have started
// playing the animation already
!isAnimationPlaying()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
index aac211a..3d8090d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragment.java
@@ -80,6 +80,10 @@
import com.android.systemui.util.CarrierConfigTracker.DefaultDataSubscriptionChangedListener;
import com.android.systemui.util.settings.SecureSettings;
+import kotlin.Unit;
+
+import kotlinx.coroutines.DisposableHandle;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -90,10 +94,6 @@
import javax.inject.Inject;
-import kotlin.Unit;
-
-import kotlinx.coroutines.DisposableHandle;
-
/**
* Contains the collapsed status bar and handles hiding/showing based on disable flags
* and keyguard state. Also manages lifecycle to make sure the views it contains are being
@@ -431,7 +431,7 @@
initOngoingCallChip();
mAnimationScheduler.addCallback(this);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.STATUS_BAR_SHOW_VIBRATE_ICON,
false,
mVolumeSettingObserver,
@@ -445,7 +445,7 @@
mStatusBarStateController.removeCallback(this);
mOngoingCallController.removeCallback(mOngoingCallListener);
mAnimationScheduler.removeCallback(this);
- mSecureSettings.unregisterContentObserver(mVolumeSettingObserver);
+ mSecureSettings.unregisterContentObserverSync(mVolumeSettingObserver);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 4bd8681..fad5df8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -138,7 +138,7 @@
}
}
};
- globalSettings.registerContentObserver(
+ globalSettings.registerContentObserverSync(
globalSettings.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS),
/* notifyForDescendants = */ false,
settingsObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
index 8b63dfe..8123f8f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceProvisionedControllerImpl.kt
@@ -42,16 +42,16 @@
import javax.inject.Inject
@SysUISingleton
-open class DeviceProvisionedControllerImpl @Inject constructor(
+open class DeviceProvisionedControllerImpl
+@Inject
+constructor(
private val secureSettings: SecureSettings,
private val globalSettings: GlobalSettings,
private val userTracker: UserTracker,
private val dumpManager: DumpManager,
@Background private val backgroundHandler: Handler,
@Main private val mainExecutor: Executor
-) : DeviceProvisionedController,
- DeviceProvisionedController.DeviceProvisionedListener,
- Dumpable {
+) : DeviceProvisionedController, DeviceProvisionedController.DeviceProvisionedListener, Dumpable {
companion object {
private const val ALL_USERS = -1
@@ -63,8 +63,7 @@
private val userSetupUri = secureSettings.getUriFor(Settings.Secure.USER_SETUP_COMPLETE)
private val deviceProvisioned = AtomicBoolean(false)
- @GuardedBy("lock")
- private val userSetupComplete = SparseBooleanArray()
+ @GuardedBy("lock") private val userSetupComplete = SparseBooleanArray()
@GuardedBy("lock")
private val listeners = ArraySet<DeviceProvisionedController.DeviceProvisionedListener>()
@@ -81,42 +80,42 @@
return _currentUser
}
- private val observer = object : ContentObserver(backgroundHandler) {
- override fun onChange(
- selfChange: Boolean,
- uris: MutableCollection<Uri>,
- flags: Int,
- userId: Int
- ) {
- val updateDeviceProvisioned = deviceProvisionedUri in uris
- val updateUser = if (userSetupUri in uris) userId else NO_USERS
- updateValues(updateDeviceProvisioned, updateUser)
- if (updateDeviceProvisioned) {
- onDeviceProvisionedChanged()
- }
- if (updateUser != NO_USERS) {
- onUserSetupChanged()
+ private val observer =
+ object : ContentObserver(backgroundHandler) {
+ override fun onChange(
+ selfChange: Boolean,
+ uris: MutableCollection<Uri>,
+ flags: Int,
+ userId: Int
+ ) {
+ val updateDeviceProvisioned = deviceProvisionedUri in uris
+ val updateUser = if (userSetupUri in uris) userId else NO_USERS
+ updateValues(updateDeviceProvisioned, updateUser)
+ if (updateDeviceProvisioned) {
+ onDeviceProvisionedChanged()
+ }
+ if (updateUser != NO_USERS) {
+ onUserSetupChanged()
+ }
}
}
- }
- private val userChangedCallback = object : UserTracker.Callback {
- @WorkerThread
- override fun onUserChanged(newUser: Int, userContext: Context) {
- updateValues(updateDeviceProvisioned = false, updateUser = newUser)
- onUserSwitched()
+ private val userChangedCallback =
+ object : UserTracker.Callback {
+ @WorkerThread
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ updateValues(updateDeviceProvisioned = false, updateUser = newUser)
+ onUserSwitched()
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {}
}
- override fun onProfilesChanged(profiles: List<UserInfo>) {}
- }
-
init {
userSetupComplete.put(currentUser, false)
}
- /**
- * Call to initialize values and register observers
- */
+ /** Call to initialize values and register observers */
open fun init() {
if (!initted.compareAndSet(false, true)) {
return
@@ -124,31 +123,39 @@
dumpManager.registerDumpable(this)
updateValues()
userTracker.addCallback(userChangedCallback, backgroundExecutor)
- globalSettings.registerContentObserver(deviceProvisionedUri, observer)
- secureSettings.registerContentObserverForUser(userSetupUri, observer, UserHandle.USER_ALL)
+ globalSettings.registerContentObserverSync(deviceProvisionedUri, observer)
+ secureSettings.registerContentObserverForUserSync(
+ userSetupUri,
+ observer,
+ UserHandle.USER_ALL
+ )
}
@WorkerThread
- private fun updateValues(
- updateDeviceProvisioned: Boolean = true,
- updateUser: Int = ALL_USERS
- ) {
+ private fun updateValues(updateDeviceProvisioned: Boolean = true, updateUser: Int = ALL_USERS) {
if (updateDeviceProvisioned) {
- deviceProvisioned
- .set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
+ deviceProvisioned.set(globalSettings.getInt(Settings.Global.DEVICE_PROVISIONED, 0) != 0)
}
synchronized(lock) {
if (updateUser == ALL_USERS) {
val n = userSetupComplete.size()
for (i in 0 until n) {
val user = userSetupComplete.keyAt(i)
- val value = secureSettings
- .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+ val value =
+ secureSettings.getIntForUser(
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0,
+ user
+ ) != 0
userSetupComplete.put(user, value)
}
} else if (updateUser != NO_USERS) {
- val value = secureSettings
- .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, updateUser) != 0
+ val value =
+ secureSettings.getIntForUser(
+ Settings.Secure.USER_SETUP_COMPLETE,
+ 0,
+ updateUser
+ ) != 0
userSetupComplete.put(updateUser, value)
}
}
@@ -160,15 +167,11 @@
* The listener will not be called when this happens.
*/
override fun addCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- synchronized(lock) {
- listeners.add(listener)
- }
+ synchronized(lock) { listeners.add(listener) }
}
override fun removeCallback(listener: DeviceProvisionedController.DeviceProvisionedListener) {
- synchronized(lock) {
- listeners.remove(listener)
- }
+ synchronized(lock) { listeners.remove(listener) }
}
override fun isDeviceProvisioned(): Boolean {
@@ -176,20 +179,14 @@
}
override fun isUserSetup(user: Int): Boolean {
- val index = synchronized(lock) {
- userSetupComplete.indexOfKey(user)
- }
+ val index = synchronized(lock) { userSetupComplete.indexOfKey(user) }
return if (index < 0) {
- val value = secureSettings
- .getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
- synchronized(lock) {
- userSetupComplete.put(user, value)
- }
+ val value =
+ secureSettings.getIntForUser(Settings.Secure.USER_SETUP_COMPLETE, 0, user) != 0
+ synchronized(lock) { userSetupComplete.put(user, value) }
value
} else {
- synchronized(lock) {
- userSetupComplete.get(user, false)
- }
+ synchronized(lock) { userSetupComplete.get(user, false) }
}
}
@@ -214,12 +211,8 @@
protected fun dispatchChange(
callback: DeviceProvisionedController.DeviceProvisionedListener.() -> Unit
) {
- val listenersCopy = synchronized(lock) {
- ArrayList(listeners)
- }
- mainExecutor.execute {
- listenersCopy.forEach(callback)
- }
+ val listenersCopy = synchronized(lock) { ArrayList(listeners) }
+ mainExecutor.execute { listenersCopy.forEach(callback) }
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
@@ -229,4 +222,4 @@
pw.println("Listeners: $listeners")
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 9eee5d0..f57b696 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -115,7 +115,7 @@
};
// Register to listen for changes in Settings.Secure settings.
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.LOCATION_SHOW_SYSTEM_OPS, mContentObserver, UserHandle.USER_ALL);
// Register to listen for changes in DeviceConfig settings.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
index 40bb67f..9ab8175 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensitiveNotificationProtectionControllerImpl.java
@@ -188,7 +188,7 @@
});
}
};
- settings.registerContentObserver(
+ settings.registerContentObserverSync(
DISABLE_SCREEN_SHARE_PROTECTIONS_FOR_APPS_AND_NOTIFICATIONS,
developerOptionsObserver);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index 600005b..e09e577 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -140,13 +140,13 @@
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (registerZenModeContentObserverBackground()) {
bgHandler.post(() -> {
- globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
- globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE_CONFIG_ETAG,
configContentObserver);
});
} else {
- globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
- globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserverSync(Global.ZEN_MODE_CONFIG_ETAG,
configContentObserver);
}
updateZenMode(getModeSettingValueFromProvider());
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 7494649..4963aae 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -444,7 +444,7 @@
filter.addAction(Intent.ACTION_WALLPAPER_CHANGED);
mBroadcastDispatcher.registerReceiver(mBroadcastReceiver, filter, mMainExecutor,
UserHandle.ALL);
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES,
false,
new ContentObserver(mBgHandler) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
index ec89610..ed52233 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxy.kt
@@ -37,7 +37,6 @@
interface SettingsProxy {
/** Returns the [ContentResolver] this instance was constructed with. */
fun getContentResolver(): ContentResolver
-
/**
* Construct the content URI for a particular name/value pair, useful for monitoring changes
* with a ContentObserver.
@@ -46,42 +45,36 @@
* @return the corresponding content URI, or null if not present
*/
fun getUriFor(name: String): Uri
-
/**
* Convenience wrapper around [ContentResolver.registerContentObserver].'
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserver(name: String, settingsObserver: ContentObserver) {
- registerContentObserver(getUriFor(name), settingsObserver)
+ fun registerContentObserverSync(name: String, settingsObserver: ContentObserver) {
+ registerContentObserverSync(getUriFor(name), settingsObserver)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver].' */
- fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) =
- registerContentObserver(uri, false, settingsObserver)
-
+ fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) =
+ registerContentObserverSync(uri, false, settingsObserver)
/**
* Convenience wrapper around [ContentResolver.registerContentObserver].'
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserver(
+ fun registerContentObserverSync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver
- ) = registerContentObserver(getUriFor(name), notifyForDescendants, settingsObserver)
-
+ ) = registerContentObserverSync(getUriFor(name), notifyForDescendants, settingsObserver)
/** Convenience wrapper around [ContentResolver.registerContentObserver].' */
- fun registerContentObserver(
+ fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver
) = getContentResolver().registerContentObserver(uri, notifyForDescendants, settingsObserver)
-
/** See [ContentResolver.unregisterContentObserver]. */
- fun unregisterContentObserver(settingsObserver: ContentObserver) =
+ fun unregisterContentObserverSync(settingsObserver: ContentObserver) =
getContentResolver().unregisterContentObserver(settingsObserver)
-
/**
* Look up a name in the database.
*
@@ -89,7 +82,6 @@
* @return the corresponding value, or null if not present
*/
fun getString(name: String): String
-
/**
* Store a name/value pair into the database.
*
@@ -98,7 +90,6 @@
* @return true if the value was set, false on database errors
*/
fun putString(name: String, value: String): Boolean
-
/**
* Store a name/value pair into the database.
*
@@ -129,7 +120,6 @@
* @see .resetToDefaults
*/
fun putString(name: String, value: String, tag: String, makeDefault: Boolean): Boolean
-
/**
* Convenience function for retrieving a single secure settings value as an integer. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -148,7 +138,6 @@
def
}
}
-
/**
* Convenience function for retrieving a single secure settings value as an integer. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -171,7 +160,6 @@
throw SettingNotFoundException(name)
}
}
-
/**
* Convenience function for updating a single settings value as an integer. This will either
* create a new entry in the table if the given name does not exist, or modify the value of the
@@ -185,7 +173,6 @@
fun putInt(name: String, value: Int): Boolean {
return putString(name, value.toString())
}
-
/**
* Convenience function for retrieving a single secure settings value as a boolean. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -199,7 +186,6 @@
fun getBool(name: String, def: Boolean): Boolean {
return getInt(name, if (def) 1 else 0) != 0
}
-
/**
* Convenience function for retrieving a single secure settings value as a boolean. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -217,7 +203,6 @@
fun getBool(name: String): Boolean {
return getInt(name) != 0
}
-
/**
* Convenience function for updating a single settings value as a boolean. This will either
* create a new entry in the table if the given name does not exist, or modify the value of the
@@ -231,7 +216,6 @@
fun putBool(name: String, value: Boolean): Boolean {
return putInt(name, if (value) 1 else 0)
}
-
/**
* Convenience function for retrieving a single secure settings value as a `long`. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -246,7 +230,6 @@
val valString = getString(name)
return parseLongOrUseDefault(valString, def)
}
-
/**
* Convenience function for retrieving a single secure settings value as a `long`. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -265,7 +248,6 @@
val valString = getString(name)
return parseLongOrThrow(name, valString)
}
-
/**
* Convenience function for updating a secure settings value as a long integer. This will either
* create a new entry in the table if the given name does not exist, or modify the value of the
@@ -279,7 +261,6 @@
fun putLong(name: String, value: Long): Boolean {
return putString(name, value.toString())
}
-
/**
* Convenience function for retrieving a single secure settings value as a floating point
* number. Note that internally setting values are always stored as strings; this function
@@ -294,7 +275,6 @@
val v = getString(name)
return parseFloat(v, def)
}
-
/**
* Convenience function for retrieving a single secure settings value as a float. Note that
* internally setting values are always stored as strings; this function converts the string to
@@ -313,7 +293,6 @@
val v = getString(name)
return parseFloatOrThrow(name, v)
}
-
/**
* Convenience function for updating a single settings value as a floating point number. This
* will either create a new entry in the table if the given name does not exist, or modify the
@@ -327,7 +306,6 @@
fun putFloat(name: String, value: Float): Boolean {
return putString(name, value.toString())
}
-
companion object {
/** Convert a string to a long, or uses a default if the string is malformed or null */
@JvmStatic
@@ -341,7 +319,6 @@
}
return value
}
-
/** Convert a string to a long, or throws an exception if the string is malformed or null */
@JvmStatic
@Throws(SettingNotFoundException::class)
@@ -355,7 +332,6 @@
throw SettingNotFoundException(name)
}
}
-
/** Convert a string to a float, or uses a default if the string is malformed or null */
@JvmStatic
fun parseFloat(v: String?, def: Float): Float {
@@ -365,7 +341,6 @@
def
}
}
-
/**
* Convert a string to a float, or throws an exception if the string is malformed or null
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
index 7484368..d757e33 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/SettingsProxyExt.kt
@@ -39,9 +39,9 @@
}
}
- names.forEach { name -> registerContentObserverForUser(name, observer, userId) }
+ names.forEach { name -> registerContentObserverForUserSync(name, observer, userId) }
- awaitClose { unregisterContentObserver(observer) }
+ awaitClose { unregisterContentObserverSync(observer) }
}
}
@@ -57,9 +57,9 @@
}
}
- names.forEach { name -> registerContentObserver(name, observer) }
+ names.forEach { name -> registerContentObserverSync(name, observer) }
- awaitClose { unregisterContentObserver(observer) }
+ awaitClose { unregisterContentObserverSync(observer) }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
index 2285270..ed13943 100644
--- a/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/settings/UserSettingsProxy.kt
@@ -41,10 +41,8 @@
* instances, unifying setting related actions in one place.
*/
interface UserSettingsProxy : SettingsProxy {
-
/** Returns that [UserTracker] this instance was constructed with. */
val userTracker: UserTracker
-
/** Returns the user id for the associated [ContentResolver]. */
var userId: Int
get() = getContentResolver().userId
@@ -53,7 +51,6 @@
"userId cannot be set in interface, use setter from an implementation instead."
)
}
-
/**
* Returns the actual current user handle when querying with the current user. Otherwise,
* returns the passed in user id.
@@ -63,63 +60,57 @@
userHandle
} else userTracker.userId
}
-
- override fun registerContentObserver(uri: Uri, settingsObserver: ContentObserver) {
- registerContentObserverForUser(uri, settingsObserver, userId)
+ override fun registerContentObserverSync(uri: Uri, settingsObserver: ContentObserver) {
+ registerContentObserverForUserSync(uri, settingsObserver, userId)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver].' */
- override fun registerContentObserver(
+ override fun registerContentObserverSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver
) {
- registerContentObserverForUser(uri, notifyForDescendants, settingsObserver, userId)
+ registerContentObserverForUserSync(uri, notifyForDescendants, settingsObserver, userId)
}
-
/**
* Convenience wrapper around [ContentResolver.registerContentObserver]
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
name: String,
settingsObserver: ContentObserver,
userHandle: Int
) {
- registerContentObserverForUser(getUriFor(name), settingsObserver, userHandle)
+ registerContentObserverForUserSync(getUriFor(name), settingsObserver, userHandle)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver] */
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
uri: Uri,
settingsObserver: ContentObserver,
userHandle: Int
) {
- registerContentObserverForUser(uri, false, settingsObserver, userHandle)
+ registerContentObserverForUserSync(uri, false, settingsObserver, userHandle)
}
-
/**
* Convenience wrapper around [ContentResolver.registerContentObserver]
*
* Implicitly calls [getUriFor] on the passed in name.
*/
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
name: String,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
userHandle: Int
) {
- registerContentObserverForUser(
+ registerContentObserverForUserSync(
getUriFor(name),
notifyForDescendants,
settingsObserver,
userHandle
)
}
-
/** Convenience wrapper around [ContentResolver.registerContentObserver] */
- fun registerContentObserverForUser(
+ fun registerContentObserverForUserSync(
uri: Uri,
notifyForDescendants: Boolean,
settingsObserver: ContentObserver,
@@ -136,7 +127,6 @@
Unit
}
}
-
/**
* Look up a name in the database.
*
@@ -146,10 +136,8 @@
override fun getString(name: String): String {
return getStringForUser(name, userId)
}
-
/** See [getString]. */
fun getStringForUser(name: String, userHandle: Int): String
-
/**
* Store a name/value pair into the database. Values written by this method will be overridden
* if a restore happens in the future.
@@ -162,10 +150,8 @@
override fun putString(name: String, value: String): Boolean {
return putStringForUser(name, value, userId)
}
-
/** Similar implementation to [putString] for the specified [userHandle]. */
fun putStringForUser(name: String, value: String, userHandle: Int): Boolean
-
/** Similar implementation to [putString] for the specified [userHandle]. */
fun putStringForUser(
name: String,
@@ -175,11 +161,9 @@
@UserIdInt userHandle: Int,
overrideableByRestore: Boolean
): Boolean
-
override fun getInt(name: String, def: Int): Int {
return getIntForUser(name, def, userId)
}
-
/** Similar implementation to [getInt] for the specified [userHandle]. */
fun getIntForUser(name: String, def: Int, userHandle: Int): Int {
val v = getStringForUser(name, userHandle)
@@ -189,10 +173,8 @@
def
}
}
-
@Throws(SettingNotFoundException::class)
override fun getInt(name: String) = getIntForUser(name, userId)
-
/** Similar implementation to [getInt] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getIntForUser(name: String, userHandle: Int): Int {
@@ -203,66 +185,52 @@
throw SettingNotFoundException(name)
}
}
-
override fun putInt(name: String, value: Int) = putIntForUser(name, value, userId)
-
/** Similar implementation to [getInt] for the specified [userHandle]. */
fun putIntForUser(name: String, value: Int, userHandle: Int) =
putStringForUser(name, value.toString(), userHandle)
-
override fun getBool(name: String, def: Boolean) = getBoolForUser(name, def, userId)
-
/** Similar implementation to [getBool] for the specified [userHandle]. */
fun getBoolForUser(name: String, def: Boolean, userHandle: Int) =
getIntForUser(name, if (def) 1 else 0, userHandle) != 0
-
@Throws(SettingNotFoundException::class)
override fun getBool(name: String) = getBoolForUser(name, userId)
-
/** Similar implementation to [getBool] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getBoolForUser(name: String, userHandle: Int): Boolean {
return getIntForUser(name, userHandle) != 0
}
-
override fun putBool(name: String, value: Boolean): Boolean {
return putBoolForUser(name, value, userId)
}
-
/** Similar implementation to [putBool] for the specified [userHandle]. */
fun putBoolForUser(name: String, value: Boolean, userHandle: Int) =
putIntForUser(name, if (value) 1 else 0, userHandle)
-
/** Similar implementation to [getLong] for the specified [userHandle]. */
fun getLongForUser(name: String, def: Long, userHandle: Int): Long {
val valString = getStringForUser(name, userHandle)
return parseLongOrUseDefault(valString, def)
}
-
/** Similar implementation to [getLong] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getLongForUser(name: String, userHandle: Int): Long {
val valString = getStringForUser(name, userHandle)
return parseLongOrThrow(name, valString)
}
-
/** Similar implementation to [putLong] for the specified [userHandle]. */
fun putLongForUser(name: String, value: Long, userHandle: Int) =
putStringForUser(name, value.toString(), userHandle)
-
/** Similar implementation to [getFloat] for the specified [userHandle]. */
fun getFloatForUser(name: String, def: Float, userHandle: Int): Float {
val v = getStringForUser(name, userHandle)
return parseFloat(v, def)
}
-
/** Similar implementation to [getFloat] for the specified [userHandle]. */
@Throws(SettingNotFoundException::class)
fun getFloatForUser(name: String, userHandle: Int): Float {
val v = getStringForUser(name, userHandle)
return parseFloatOrThrow(name, v)
}
-
/** Similar implementation to [putFloat] for the specified [userHandle]. */
fun putFloatForUser(name: String, value: Float, userHandle: Int) =
putStringForUser(name, value.toString(), userHandle)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
index e0f64b4..154737c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractor.kt
@@ -17,15 +17,18 @@
package com.android.systemui.volume.domain.interactor
import android.bluetooth.BluetoothAdapter
+import android.content.Context
import android.media.AudioDeviceInfo
import com.android.settingslib.bluetooth.CachedBluetoothDevice
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.BluetoothMediaDevice
import com.android.settingslib.media.MediaDevice
import com.android.settingslib.media.MediaDevice.MediaDeviceType
+import com.android.settingslib.media.PhoneMediaDevice
import com.android.settingslib.volume.data.repository.AudioRepository
import com.android.settingslib.volume.data.repository.AudioSharingRepository
import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.volume.domain.model.AudioOutputDevice
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
@@ -36,6 +39,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
@@ -47,6 +51,7 @@
class AudioOutputInteractor
@Inject
constructor(
+ @Application private val context: Context,
audioRepository: AudioRepository,
audioModeInteractor: AudioModeInteractor,
@VolumePanelScope scope: CoroutineScope,
@@ -58,7 +63,7 @@
audioSharingRepository: AudioSharingRepository,
) {
- val currentAudioDevice: Flow<AudioOutputDevice> =
+ val currentAudioDevice: StateFlow<AudioOutputDevice> =
audioModeInteractor.isOngoingCall
.flatMapLatest { isOngoingCall ->
if (isOngoingCall) {
@@ -102,7 +107,7 @@
)
}
return AudioOutputDevice.BuiltIn(
- name = productName.toString(),
+ name = PhoneMediaDevice.getMediaTransferThisDeviceName(context),
icon = deviceIconInteractor.loadIcon(type),
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
index 199bc3b..333f4ad 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputActionsInteractor.kt
@@ -21,7 +21,7 @@
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.animation.Expandable
import com.android.systemui.media.dialog.MediaOutputDialogManager
-import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import javax.inject.Inject
@@ -29,14 +29,12 @@
@VolumePanelScope
class MediaOutputActionsInteractor
@Inject
-constructor(
- private val mediaOutputDialogManager: MediaOutputDialogManager,
-) {
+constructor(private val mediaOutputDialogManager: MediaOutputDialogManager) {
- fun onBarClick(sessionWithPlaybackState: SessionWithPlaybackState?, expandable: Expandable?) {
- if (sessionWithPlaybackState?.isPlaybackActive == true) {
+ fun onBarClick(model: MediaOutputComponentModel?, expandable: Expandable?) {
+ if (model is MediaOutputComponentModel.MediaSession) {
mediaOutputDialogManager.createAndShowWithController(
- sessionWithPlaybackState.session.packageName,
+ model.session.packageName,
false,
expandable?.dialogController()
)
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
new file mode 100644
index 0000000..ed25129
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractor.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
+import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
+import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
+import com.android.systemui.volume.panel.shared.model.Result
+import com.android.systemui.volume.panel.shared.model.filterData
+import com.android.systemui.volume.panel.shared.model.wrapInResult
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.mapNotNull
+import kotlinx.coroutines.flow.stateIn
+
+/** Gathers together a domain state for the Media Output Volume Panel component. */
+@OptIn(ExperimentalCoroutinesApi::class)
+@VolumePanelScope
+class MediaOutputComponentInteractor
+@Inject
+constructor(
+ @VolumePanelScope private val coroutineScope: CoroutineScope,
+ private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
+ audioOutputInteractor: AudioOutputInteractor,
+ audioModeInteractor: AudioModeInteractor,
+ interactor: MediaOutputInteractor,
+) {
+
+ private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
+ interactor.defaultActiveMediaSession
+ .filterData()
+ .flatMapLatest { session ->
+ if (session == null) {
+ flowOf(null)
+ } else {
+ mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
+ playback?.let { SessionWithPlaybackState(session, playback.isActive) }
+ }
+ }
+ }
+ .wrapInResult()
+ .stateIn(
+ coroutineScope,
+ SharingStarted.Eagerly,
+ Result.Loading(),
+ )
+
+ private val currentAudioDevice: Flow<AudioOutputDevice> =
+ audioOutputInteractor.currentAudioDevice.filter { it !is AudioOutputDevice.Unknown }
+
+ val mediaOutputModel: StateFlow<Result<MediaOutputComponentModel>> =
+ audioModeInteractor.isOngoingCall
+ .flatMapLatest { isOngoingCall ->
+ audioOutputInteractor.isInAudioSharing.flatMapLatest { isInAudioSharing ->
+ if (isOngoingCall) {
+ currentAudioDevice.map {
+ MediaOutputComponentModel.Calling(it, isInAudioSharing)
+ }
+ } else {
+ combine(sessionWithPlaybackState.filterData(), currentAudioDevice) {
+ sessionWithPlaybackState,
+ currentAudioDevice ->
+ if (sessionWithPlaybackState == null) {
+ MediaOutputComponentModel.Idle(currentAudioDevice, isInAudioSharing)
+ } else {
+ MediaOutputComponentModel.MediaSession(
+ sessionWithPlaybackState.session,
+ sessionWithPlaybackState.isPlaybackActive,
+ currentAudioDevice,
+ isInAudioSharing,
+ )
+ }
+ }
+ }
+ }
+ }
+ .wrapInResult()
+ .stateIn(coroutineScope, SharingStarted.Eagerly, Result.Loading())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
new file mode 100644
index 0000000..220fb2b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/domain/model/MediaOutputComponentModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.model
+
+import com.android.systemui.volume.domain.model.AudioOutputDevice
+import com.android.systemui.volume.panel.component.mediaoutput.shared.model.MediaDeviceSession
+
+/** Models domain data for the Media Output Component */
+sealed interface MediaOutputComponentModel {
+
+ val device: AudioOutputDevice
+ val isInAudioSharing: Boolean
+
+ /** There is an ongoing call on the device. */
+ data class Calling(
+ override val device: AudioOutputDevice,
+ override val isInAudioSharing: Boolean,
+ ) : MediaOutputComponentModel
+
+ /** There is media playing on the device. */
+ data class MediaSession(
+ val session: MediaDeviceSession,
+ val isPlaybackActive: Boolean,
+ override val device: AudioOutputDevice,
+ override val isInAudioSharing: Boolean,
+ ) : MediaOutputComponentModel
+
+ /** There is nothing playing on the device. */
+ data class Idle(
+ override val device: AudioOutputDevice,
+ override val isInAudioSharing: Boolean,
+ ) : MediaOutputComponentModel
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
index 40b7977..36b42f2 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModel.kt
@@ -18,33 +18,24 @@
import android.content.Context
import com.android.internal.logging.UiEventLogger
-import com.android.settingslib.volume.domain.interactor.AudioModeInteractor
import com.android.systemui.animation.Expandable
import com.android.systemui.common.shared.model.Color
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.res.R
-import com.android.systemui.volume.domain.interactor.AudioOutputInteractor
import com.android.systemui.volume.domain.model.AudioOutputDevice
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaDeviceSessionInteractor
import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputActionsInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputInteractor
-import com.android.systemui.volume.panel.component.mediaoutput.shared.model.SessionWithPlaybackState
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.MediaOutputComponentInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.model.MediaOutputComponentModel
import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope
import com.android.systemui.volume.panel.shared.model.Result
import com.android.systemui.volume.panel.shared.model.filterData
-import com.android.systemui.volume.panel.shared.model.wrapInResult
import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.filter
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.mapNotNull
import kotlinx.coroutines.flow.stateIn
/** Models the UI of the Media Output Volume Panel component. */
@@ -56,61 +47,40 @@
private val context: Context,
@VolumePanelScope private val coroutineScope: CoroutineScope,
private val actionsInteractor: MediaOutputActionsInteractor,
- private val mediaDeviceSessionInteractor: MediaDeviceSessionInteractor,
- private val audioOutputInteractor: AudioOutputInteractor,
- audioModeInteractor: AudioModeInteractor,
- interactor: MediaOutputInteractor,
+ private val mediaOutputComponentInteractor: MediaOutputComponentInteractor,
private val uiEventLogger: UiEventLogger,
) {
- private val sessionWithPlaybackState: StateFlow<Result<SessionWithPlaybackState?>> =
- interactor.defaultActiveMediaSession
- .filterData()
- .flatMapLatest { session ->
- if (session == null) {
- flowOf(null)
- } else {
- mediaDeviceSessionInteractor.playbackState(session).mapNotNull { playback ->
- playback?.let { SessionWithPlaybackState(session, playback.isActive) }
- }
- }
- }
- .wrapInResult()
- .stateIn(
- coroutineScope,
- SharingStarted.Eagerly,
- Result.Loading(),
- )
-
val connectedDeviceViewModel: StateFlow<ConnectedDeviceViewModel?> =
- combine(
- sessionWithPlaybackState.filterData(),
- audioModeInteractor.isOngoingCall,
- audioOutputInteractor.currentAudioDevice.filter {
- it !is AudioOutputDevice.Unknown
- },
- audioOutputInteractor.isInAudioSharing,
- ) { mediaDeviceSession, isOngoingCall, currentConnectedDevice, isInAudioSharing ->
+ mediaOutputComponentInteractor.mediaOutputModel
+ .filterData()
+ .map { mediaOutputModel ->
val label =
- when {
- isOngoingCall -> context.getString(R.string.media_output_title_ongoing_call)
- mediaDeviceSession?.isPlaybackActive == true ->
- context.getString(
- R.string.media_output_label_title,
- mediaDeviceSession.session.appLabel
- )
- else -> context.getString(R.string.media_output_title_without_playing)
+ when (mediaOutputModel) {
+ is MediaOutputComponentModel.Idle -> {
+ context.getString(R.string.media_output_title_without_playing)
+ }
+ is MediaOutputComponentModel.MediaSession -> {
+ if (mediaOutputModel.isPlaybackActive) {
+ context.getString(
+ R.string.media_output_label_title,
+ mediaOutputModel.session.appLabel,
+ )
+ } else {
+ context.getString(R.string.media_output_title_without_playing)
+ }
+ }
+ is MediaOutputComponentModel.Calling -> {
+ context.getString(R.string.media_output_title_ongoing_call)
+ }
}
ConnectedDeviceViewModel(
label,
- when (isInAudioSharing) {
- true -> {
- context.getString(R.string.audio_sharing_description)
- }
- false -> {
- currentConnectedDevice.name
- }
- }
+ if (mediaOutputModel.isInAudioSharing) {
+ context.getString(R.string.audio_sharing_description)
+ } else {
+ mediaOutputModel.device.name
+ },
)
}
.stateIn(
@@ -120,16 +90,20 @@
)
val deviceIconViewModel: StateFlow<DeviceIconViewModel?> =
- combine(sessionWithPlaybackState.filterData(), audioOutputInteractor.currentAudioDevice) {
- mediaDeviceSession,
- currentConnectedDevice ->
+ mediaOutputComponentInteractor.mediaOutputModel
+ .filterData()
+ .map { mediaOutputModel ->
val icon: Icon =
- currentConnectedDevice
- .takeIf { currentConnectedDevice !is AudioOutputDevice.Unknown }
+ mediaOutputModel.device
+ .takeIf { it !is AudioOutputDevice.Unknown }
?.icon
?.let { Icon.Loaded(it, null) }
?: Icon.Resource(R.drawable.ic_media_home_devices, null)
- if (mediaDeviceSession?.isPlaybackActive == true) {
+ val isPlaybackActive =
+ (mediaOutputModel as? MediaOutputComponentModel.MediaSession)
+ ?.isPlaybackActive == true
+ val isCalling = mediaOutputModel is MediaOutputComponentModel.Calling
+ if (isPlaybackActive || isCalling) {
DeviceIconViewModel.IsPlaying(
icon = icon,
iconColor =
@@ -156,8 +130,9 @@
)
val enabled: StateFlow<Boolean> =
- audioOutputInteractor.isInAudioSharing
- .map { !it }
+ mediaOutputComponentInteractor.mediaOutputModel
+ .filterData()
+ .map { !it.isInAudioSharing }
.stateIn(
coroutineScope,
SharingStarted.Eagerly,
@@ -166,7 +141,11 @@
fun onBarClick(expandable: Expandable?) {
uiEventLogger.log(VolumePanelUiEvent.VOLUME_PANEL_MEDIA_OUTPUT_CLICKED)
- val result = sessionWithPlaybackState.value
- actionsInteractor.onBarClick((result as? Result.Data)?.data, expandable)
+ val result: Result<MediaOutputComponentModel> =
+ mediaOutputComponentInteractor.mediaOutputModel.value
+ actionsInteractor.onBarClick(
+ (result as? Result.Data<MediaOutputComponentModel>)?.data,
+ expandable
+ )
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
index 4b2d26a..6c6a1cc 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt
@@ -48,15 +48,31 @@
private val uiEventLogger: UiEventLogger,
) {
+ private val spatialSpeakerIcon =
+ Icon.Resource(R.drawable.ic_spatial_speaker, contentDescription = null)
+
val spatialAudioButton: StateFlow<ButtonViewModel?> =
- interactor.isEnabled
- .map {
- val isChecked = it is SpatialAudioEnabledModel.SpatialAudioEnabled
- it.toViewModel(isChecked)
+ combine(interactor.isEnabled, interactor.isAvailable) { isEnabled, isAvailable ->
+ isEnabled
+ .toViewModel(
+ isChecked = isEnabled is SpatialAudioEnabledModel.SpatialAudioEnabled,
+ isHeadTrackingAvailable =
+ isAvailable is SpatialAudioAvailabilityModel.SpatialAudio,
+ )
.copy(label = context.getString(R.string.volume_panel_spatial_audio_title))
}
.stateIn(scope, SharingStarted.Eagerly, null)
+ val shouldUsePopup: StateFlow<Boolean> =
+ interactor.isAvailable
+ .map {
+ // head tracking availability means there are three possible states for the spatial
+ // audio: disabled, enabled regular, enabled with head tracking.
+ // Show popup in this case instead of a togglealbe button.
+ it is SpatialAudioAvailabilityModel.SpatialAudio
+ }
+ .stateIn(scope, SharingStarted.Eagerly, false)
+
val isAvailable: StateFlow<Boolean> =
availabilityCriteria.isAvailable().stateIn(scope, SharingStarted.Eagerly, true)
@@ -73,8 +89,12 @@
}
}
.map { isEnabled ->
- val isChecked = isEnabled == currentIsEnabled
- val buttonViewModel: ButtonViewModel = isEnabled.toViewModel(isChecked)
+ val buttonViewModel: ButtonViewModel =
+ isEnabled.toViewModel(
+ isChecked = isEnabled == currentIsEnabled,
+ isHeadTrackingAvailable =
+ isAvailable is SpatialAudioAvailabilityModel.HeadTracking,
+ )
SpatialAudioButtonViewModel(button = buttonViewModel, model = isEnabled)
}
}
@@ -97,11 +117,21 @@
scope.launch { interactor.setEnabled(model) }
}
- private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ButtonViewModel {
+ private fun SpatialAudioEnabledModel.toViewModel(
+ isChecked: Boolean,
+ isHeadTrackingAvailable: Boolean,
+ ): ButtonViewModel {
+ // This method deliberately uses the same icon for the case when head tracking is disabled
+ // to show a toggle button with a non-changing icon
if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) {
return ButtonViewModel(
isActive = isChecked,
- icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null),
+ icon =
+ if (isHeadTrackingAvailable) {
+ Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null)
+ } else {
+ spatialSpeakerIcon
+ },
label = context.getString(R.string.volume_panel_spatial_audio_tracking)
)
}
@@ -109,7 +139,12 @@
if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) {
return ButtonViewModel(
isActive = isChecked,
- icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null),
+ icon =
+ if (isHeadTrackingAvailable) {
+ Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null)
+ } else {
+ spatialSpeakerIcon
+ },
label = context.getString(R.string.volume_panel_spatial_audio_fixed)
)
}
@@ -117,7 +152,12 @@
if (this is SpatialAudioEnabledModel.Disabled) {
return ButtonViewModel(
isActive = isChecked,
- icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null),
+ icon =
+ if (isHeadTrackingAvailable) {
+ Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null)
+ } else {
+ spatialSpeakerIcon
+ },
label = context.getString(R.string.volume_panel_spatial_audio_off)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index ff18418..1d32a4f 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -149,12 +149,12 @@
if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) {
mWalletPreferenceChangeEvents--;
if (mWalletPreferenceChangeEvents == 0) {
- mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
+ mSecureSettings.unregisterContentObserverSync(mWalletPreferenceObserver);
}
} else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) {
mDefaultPaymentAppChangeEvents--;
if (mDefaultPaymentAppChangeEvents == 0) {
- mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver);
+ mSecureSettings.unregisterContentObserverSync(mDefaultPaymentAppObserver);
}
} else if (event == DEFAULT_WALLET_APP_CHANGE && mDefaultWalletAppObserver != null) {
mDefaultWalletAppChangeEvents--;
@@ -312,7 +312,7 @@
}
};
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT,
false /* notifyForDescendants */,
mDefaultPaymentAppObserver,
@@ -351,7 +351,7 @@
}
};
- mSecureSettings.registerContentObserverForUser(
+ mSecureSettings.registerContentObserverForUserSync(
QuickAccessWalletClientImpl.SETTING_KEY,
false /* notifyForDescendants */,
mWalletPreferenceObserver,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 99b5a4b..cfa74f0 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -181,7 +181,7 @@
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
mExecutor.runAllReady();
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(Settings.Secure.LOCKSCREEN_USE_DOUBLE_LINE_CLOCK),
anyBoolean(), observerCaptor.capture(), eq(UserHandle.USER_ALL));
ContentObserver observer = observerCaptor.getValue();
@@ -247,7 +247,7 @@
ArgumentCaptor.forClass(ContentObserver.class);
mController.init();
mExecutor.runAllReady();
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(Settings.Secure.LOCK_SCREEN_WEATHER_ENABLED), anyBoolean(),
observerCaptor.capture(), eq(UserHandle.USER_ALL));
ContentObserver observer = observerCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
index 92b06ba..138fed2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationSettingsTest.java
@@ -393,7 +393,7 @@
mWindowMagnificationSettings.showSettingPanel();
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(ACCESSIBILITY_MAGNIFICATION_CAPABILITY),
any(ContentObserver.class),
eq(UserHandle.USER_CURRENT));
@@ -408,7 +408,7 @@
mWindowMagnificationSettings.showSettingPanel();
mWindowMagnificationSettings.hideSettingPanel();
- verify(mSecureSettings).unregisterContentObserver(any(ContentObserver.class));
+ verify(mSecureSettings).unregisterContentObserverSync(any(ContentObserver.class));
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
index 9ba56d2..6391986 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/data/repository/FaceSettingsRepositoryImplTest.kt
@@ -82,7 +82,12 @@
val keys =
captureMany<String> {
verify(secureSettings)
- .registerContentObserverForUser(capture(), anyBoolean(), any(), eq(USER_ID))
+ .registerContentObserverForUserSync(
+ capture(),
+ anyBoolean(),
+ any(),
+ eq(USER_ID)
+ )
}
assertThat(keys).containsExactly(FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION)
@@ -102,7 +107,7 @@
val observer =
withArgCaptor<ContentObserver> {
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(setting),
anyBoolean(),
capture(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
index 590989d..154c373 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/settings/ControlsSettingsDialogManagerImplTest.kt
@@ -332,7 +332,7 @@
}
private fun attachRepositoryToSettings() {
- secureSettings.registerContentObserver(
+ secureSettings.registerContentObserverSync(
SETTING_SHOW,
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
@@ -343,7 +343,7 @@
}
)
- secureSettings.registerContentObserver(
+ secureSettings.registerContentObserverSync(
SETTING_ACTION,
object : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index 16dd9af..e33d75c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -50,7 +50,6 @@
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancesMetricsLogger
-import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testDispatcher
import com.android.systemui.kosmos.testScope
import com.android.systemui.plugins.ActivityStarter
@@ -290,7 +289,6 @@
underTest =
KeyguardQuickAffordancesCombinedViewModel(
- applicationScope = kosmos.applicationCoroutineScope,
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
index 3ca5b42..f7b3f2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaCarouselControllerTest.kt
@@ -196,7 +196,7 @@
whenever(mediaFlags.isPersistentSsCardEnabled()).thenReturn(false)
MediaPlayerData.clear()
verify(globalSettings)
- .registerContentObserver(
+ .registerContentObserverSync(
eq(Settings.Global.getUriFor(Settings.Global.ANIMATOR_DURATION_SCALE)),
capture(settingsObserverCaptor)
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 5e14b1a..820c3de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -175,7 +175,7 @@
mTileLifecycleManagerFactory, mUserFileManager, mQSPipelineFlagsRepository);
mMainExecutor.runAllReady();
- mSecureSettings.registerContentObserverForUser(SETTING, new ContentObserver(null) {
+ mSecureSettings.registerContentObserverForUserSync(SETTING, new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
index 8012768..dc9c22f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorTest.java
@@ -320,7 +320,7 @@
.thenReturn(1);
ArgumentCaptor<ContentObserver> contentObserverCaptor = ArgumentCaptor.forClass(
ContentObserver.class);
- verify(mSecureSettings).registerContentObserverForUser(eq(SHOW_NOTIFICATION_SNOOZE),
+ verify(mSecureSettings).registerContentObserverForUserSync(eq(SHOW_NOTIFICATION_SNOOZE),
contentObserverCaptor.capture(), anyInt());
ContentObserver contentObserver = contentObserverCaptor.getValue();
contentObserver.onChange(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
index 7943872..2f77b33 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/inflation/NotifUiAdjustmentProviderTest.kt
@@ -69,22 +69,21 @@
private val groupMembershipManager: GroupMembershipManager = mock()
private val section = NotifSection(mock(), 0)
- private val entry = NotificationEntryBuilder()
- .setSection(section)
- .setParent(GroupEntry.ROOT_ENTRY)
- .build()
+ private val entry =
+ NotificationEntryBuilder().setSection(section).setParent(GroupEntry.ROOT_ENTRY).build()
private lateinit var contentObserver: ContentObserver
- private val adjustmentProvider = NotifUiAdjustmentProvider(
- handler,
- secureSettings,
- lockscreenUserManager,
- sensitiveNotifProtectionController,
- sectionStyleProvider,
- userTracker,
- groupMembershipManager,
- )
+ private val adjustmentProvider =
+ NotifUiAdjustmentProvider(
+ handler,
+ secureSettings,
+ lockscreenUserManager,
+ sensitiveNotifProtectionController,
+ sectionStyleProvider,
+ userTracker,
+ groupMembershipManager,
+ )
@Before
fun setup() {
@@ -92,9 +91,8 @@
adjustmentProvider.addDirtyListener(dirtyListener)
verify(secureSettings).getIntForUser(eq(SHOW_NOTIFICATION_SNOOZE), any(), any())
contentObserver = withArgCaptor {
- verify(secureSettings).registerContentObserverForUser(
- eq(SHOW_NOTIFICATION_SNOOZE), capture(), any()
- )
+ verify(secureSettings)
+ .registerContentObserverForUserSync(eq(SHOW_NOTIFICATION_SNOOZE), capture(), any())
}
verifyNoMoreInteractions(secureSettings, dirtyListener)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
index 352b79f..310fa67 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationSettingsControllerTest.kt
@@ -102,6 +102,7 @@
verify(userTracker).addCallback(any(), any())
verify(dumpManager).registerNormalDumpable(anyString(), eq(controller))
}
+
@Test
fun updateContentObserverRegistration_onUserChange_noSettingsListeners() {
verify(userTracker).addCallback(capture(userTrackerCallbackCaptor), any())
@@ -112,10 +113,11 @@
userCallback.onUserChanged(userId, context)
// Validate: Nothing to do, since we aren't monitoring settings
- verify(secureSettings, never()).unregisterContentObserver(any())
+ verify(secureSettings, never()).unregisterContentObserverSync(any())
verify(secureSettings, never())
- .registerContentObserverForUser(any(Uri::class.java), anyBoolean(), any(), anyInt())
+ .registerContentObserverForUserSync(any(Uri::class.java), anyBoolean(), any(), anyInt())
}
+
@Test
fun updateContentObserverRegistration_onUserChange_withSettingsListeners() {
// When: someone is listening to a setting
@@ -129,9 +131,9 @@
userCallback.onUserChanged(userId, context)
// Validate: The tracker is unregistered and re-registered with the new user
- verify(secureSettings).unregisterContentObserver(any())
+ verify(secureSettings).unregisterContentObserverSync(any())
verify(secureSettings)
- .registerContentObserverForUser(eq(settingUri1), eq(false), any(), eq(userId))
+ .registerContentObserverForUserSync(eq(settingUri1), eq(false), any(), eq(userId))
}
@Test
@@ -140,7 +142,7 @@
verifyZeroInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri1),
eq(false),
any(),
@@ -149,7 +151,7 @@
controller.addCallback(settingUri1, Mockito.mock(Listener::class.java))
verify(secureSettings)
- .registerContentObserverForUser(any(Uri::class.java), anyBoolean(), any(), anyInt())
+ .registerContentObserverForUserSync(any(Uri::class.java), anyBoolean(), any(), anyInt())
}
@Test
@@ -158,7 +160,7 @@
verifyZeroInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri1),
eq(false),
any(),
@@ -170,7 +172,7 @@
verifyNoMoreInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri2),
eq(false),
any(),
@@ -186,7 +188,7 @@
verifyZeroInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
eq(settingUri1),
eq(false),
any(),
@@ -198,18 +200,18 @@
verifyNoMoreInteractions(secureSettings)
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(eq(settingUri2), anyBoolean(), any(), anyInt())
+ .registerContentObserverForUserSync(eq(settingUri2), anyBoolean(), any(), anyInt())
clearInvocations(secureSettings)
controller.removeCallback(settingUri2, listenerSetting2)
testableLooper.processAllMessages()
- verify(secureSettings, never()).unregisterContentObserver(any())
+ verify(secureSettings, never()).unregisterContentObserverSync(any())
clearInvocations(secureSettings)
controller.removeCallback(settingUri1, listenerSetting1)
verifyNoMoreInteractions(secureSettings)
testableLooper.processAllMessages()
- verify(secureSettings).unregisterContentObserver(any())
+ verify(secureSettings).unregisterContentObserverSync(any())
}
@Test
@@ -253,7 +255,7 @@
testableLooper.processAllMessages()
verify(secureSettings)
- .registerContentObserverForUser(
+ .registerContentObserverForUserSync(
any(Uri::class.java),
anyBoolean(),
capture(settingsObserverCaptor),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
index 5ad88ad..d2c1dd5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java
@@ -202,7 +202,7 @@
verify(mWakefulnessLifecycle).addObserver(mWakefulnessLifecycleObserver.capture());
verify(mDumpManager).registerDumpable(any(), any());
verify(mDeviceProvisionedController).addCallback(mDeviceProvisionedListener.capture());
- verify(mSecureSettings).registerContentObserverForUser(
+ verify(mSecureSettings).registerContentObserverForUserSync(
eq(Settings.Secure.THEME_CUSTOMIZATION_OVERLAY_PACKAGES),
eq(false), mSettingsObserver.capture(), eq(UserHandle.USER_ALL)
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
index 99f6303..25a44e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/FakeSettingsTest.java
@@ -83,7 +83,7 @@
@Test
public void testRegisterContentObserver() {
- mFakeSettings.registerContentObserver("cat", mContentObserver);
+ mFakeSettings.registerContentObserverSync("cat", mContentObserver);
mFakeSettings.putString("cat", "hat");
@@ -93,7 +93,7 @@
@Test
public void testRegisterContentObserverAllUsers() {
- mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.registerContentObserverForUserSync(
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
mFakeSettings.putString("cat", "hat");
@@ -104,8 +104,8 @@
@Test
public void testUnregisterContentObserver() {
- mFakeSettings.registerContentObserver("cat", mContentObserver);
- mFakeSettings.unregisterContentObserver(mContentObserver);
+ mFakeSettings.registerContentObserverSync("cat", mContentObserver);
+ mFakeSettings.unregisterContentObserverSync(mContentObserver);
mFakeSettings.putString("cat", "hat");
@@ -115,9 +115,9 @@
@Test
public void testUnregisterContentObserverAllUsers() {
- mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.registerContentObserverForUserSync(
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL);
- mFakeSettings.unregisterContentObserver(mContentObserver);
+ mFakeSettings.unregisterContentObserverSync(mContentObserver);
mFakeSettings.putString("cat", "hat");
@@ -128,7 +128,7 @@
@Test
public void testContentObserverDispatchCorrectUser() {
int user = 10;
- mFakeSettings.registerContentObserverForUser(
+ mFakeSettings.registerContentObserverForUserSync(
mFakeSettings.getUriFor("cat"), false, mContentObserver, UserHandle.USER_ALL
);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
index ab95707..eb11e38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/SettingsProxyTest.kt
@@ -52,14 +52,14 @@
@Test
fun registerContentObserver_inputString_success() {
- mSettings.registerContentObserver(TEST_SETTING, mContentObserver)
+ mSettings.registerContentObserverSync(TEST_SETTING, mContentObserver)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
}
@Test
fun registerContentObserver_inputString_notifyForDescendants_true() {
- mSettings.registerContentObserver(
+ mSettings.registerContentObserverSync(
TEST_SETTING,
notifyForDescendants = true,
mContentObserver
@@ -70,14 +70,14 @@
@Test
fun registerContentObserver_inputUri_success() {
- mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+ mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver))
}
@Test
fun registerContentObserver_inputUri_notifyForDescendants_true() {
- mSettings.registerContentObserver(
+ mSettings.registerContentObserverSync(
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver
@@ -88,7 +88,7 @@
@Test
fun unregisterContentObserver() {
- mSettings.unregisterContentObserver(mContentObserver)
+ mSettings.unregisterContentObserverSync(mContentObserver)
verify(mSettings.getContentResolver()).unregisterContentObserver(eq(mContentObserver))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
index 56328b9..38469ee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/settings/UserSettingsProxyTest.kt
@@ -58,7 +58,7 @@
@Test
fun registerContentObserverForUser_inputString_success() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING,
mContentObserver,
mUserTracker.userId
@@ -74,7 +74,7 @@
@Test
fun registerContentObserverForUser_inputString_notifyForDescendants_true() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING,
notifyForDescendants = true,
mContentObserver,
@@ -91,7 +91,7 @@
@Test
fun registerContentObserverForUser_inputUri_success() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING_URI,
mContentObserver,
mUserTracker.userId
@@ -107,7 +107,7 @@
@Test
fun registerContentObserverForUser_inputUri_notifyForDescendants_true() {
- mSettings.registerContentObserverForUser(
+ mSettings.registerContentObserverForUserSync(
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver,
@@ -124,14 +124,14 @@
@Test
fun registerContentObserver_inputUri_success() {
- mSettings.registerContentObserver(TEST_SETTING_URI, mContentObserver)
+ mSettings.registerContentObserverSync(TEST_SETTING_URI, mContentObserver)
verify(mSettings.getContentResolver())
.registerContentObserver(eq(TEST_SETTING_URI), eq(false), eq(mContentObserver), eq(0))
}
@Test
fun registerContentObserver_inputUri_notifyForDescendants_true() {
- mSettings.registerContentObserver(
+ mSettings.registerContentObserverSync(
TEST_SETTING_URI,
notifyForDescendants = true,
mContentObserver
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
index cd2710e..fb983f7 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractorKosmos.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.kosmos.testDispatcher
import com.android.systemui.settings.userTracker
import com.android.systemui.user.domain.interactor.selectedUserInteractor
import com.android.systemui.util.mockito.mock
@@ -29,6 +30,7 @@
CommunalSettingsInteractor(
bgScope = applicationCoroutineScope,
bgExecutor = fakeExecutor,
+ bgDispatcher = testDispatcher,
repository = communalSettingsRepository,
userInteractor = selectedUserInteractor,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
index beabaf5..3a70cdf 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeGlobalSettings.java
@@ -44,7 +44,7 @@
}
@Override
- public void registerContentObserver(Uri uri, boolean notifyDescendants,
+ public void registerContentObserverSync(Uri uri, boolean notifyDescendants,
ContentObserver settingsObserver) {
List<ContentObserver> observers;
mContentObserversAllUsers.putIfAbsent(uri.toString(), new ArrayList<>());
@@ -53,7 +53,7 @@
}
@Override
- public void unregisterContentObserver(ContentObserver settingsObserver) {
+ public void unregisterContentObserverSync(ContentObserver settingsObserver) {
for (Map.Entry<String, List<ContentObserver>> entry :
mContentObserversAllUsers.entrySet()) {
entry.getValue().remove(settingsObserver);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
index a491886..cd219ec 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/settings/FakeSettings.java
@@ -66,7 +66,7 @@
}
@Override
- public void registerContentObserverForUser(Uri uri, boolean notifyDescendants,
+ public void registerContentObserverForUserSync(Uri uri, boolean notifyDescendants,
ContentObserver settingsObserver, int userHandle) {
List<ContentObserver> observers;
if (userHandle == UserHandle.USER_ALL) {
@@ -81,7 +81,7 @@
}
@Override
- public void unregisterContentObserver(ContentObserver settingsObserver) {
+ public void unregisterContentObserverSync(ContentObserver settingsObserver) {
for (SettingsKey key : mContentObservers.keySet()) {
List<ContentObserver> observers = mContentObservers.get(key);
observers.remove(settingsObserver);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
index 3f51a79..e2d414e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/domain/interactor/AudioOutputInteractorKosmos.kt
@@ -16,6 +16,7 @@
package com.android.systemui.volume.domain.interactor
+import android.content.applicationContext
import com.android.systemui.bluetooth.bluetoothAdapter
import com.android.systemui.bluetooth.localBluetoothManager
import com.android.systemui.kosmos.Kosmos
@@ -27,6 +28,7 @@
val Kosmos.audioOutputInteractor by
Kosmos.Fixture {
AudioOutputInteractor(
+ applicationContext,
audioRepository,
audioModeInteractor,
testScope.backgroundScope,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
new file mode 100644
index 0000000..9f11822
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/domain/interactor/MediaOutputComponentInteractorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume.panel.component.mediaoutput.domain.interactor
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.volume.domain.interactor.audioModeInteractor
+import com.android.systemui.volume.domain.interactor.audioOutputInteractor
+import com.android.systemui.volume.mediaDeviceSessionInteractor
+import com.android.systemui.volume.mediaOutputInteractor
+
+val Kosmos.mediaOutputComponentInteractor by
+ Kosmos.Fixture {
+ MediaOutputComponentInteractor(
+ testScope.backgroundScope,
+ mediaDeviceSessionInteractor,
+ audioOutputInteractor,
+ audioModeInteractor,
+ mediaOutputInteractor,
+ )
+ }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
index 6d4576e..2cd6ff2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/panel/component/mediaoutput/ui/viewmodel/MediaOutputViewModelKosmos.kt
@@ -20,11 +20,8 @@
import com.android.internal.logging.uiEventLogger
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.volume.domain.interactor.audioModeInteractor
-import com.android.systemui.volume.domain.interactor.audioOutputInteractor
-import com.android.systemui.volume.mediaDeviceSessionInteractor
import com.android.systemui.volume.mediaOutputActionsInteractor
-import com.android.systemui.volume.mediaOutputInteractor
+import com.android.systemui.volume.panel.component.mediaoutput.domain.interactor.mediaOutputComponentInteractor
var Kosmos.mediaOutputViewModel by
Kosmos.Fixture {
@@ -32,10 +29,7 @@
applicationContext,
testScope.backgroundScope,
mediaOutputActionsInteractor,
- mediaDeviceSessionInteractor,
- audioOutputInteractor,
- audioModeInteractor,
- mediaOutputInteractor,
+ mediaOutputComponentInteractor,
uiEventLogger,
)
}
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 2184340..b9cdf27 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -1005,7 +1005,7 @@
if (isForeground || foregroundId != 0) {
pw.print(prefix); pw.print("isForeground="); pw.print(isForeground);
pw.print(" foregroundId="); pw.print(foregroundId);
- pw.printf(" types=%08X", foregroundServiceType);
+ pw.printf(" types=0x%08X", foregroundServiceType);
pw.print(" foregroundNoti="); pw.println(foregroundNoti);
if (isShortFgs() && mShortFgsInfo != null) {
diff --git a/services/core/java/com/android/server/display/DisplayAdapter.java b/services/core/java/com/android/server/display/DisplayAdapter.java
index 5690a9e..69bc66f 100644
--- a/services/core/java/com/android/server/display/DisplayAdapter.java
+++ b/services/core/java/com/android/server/display/DisplayAdapter.java
@@ -135,7 +135,7 @@
float[] alternativeRefreshRates,
@Display.HdrCapabilities.HdrType int[] supportedHdrTypes) {
return new Display.Mode(NEXT_DISPLAY_MODE_ID.getAndIncrement(), width, height, refreshRate,
- vsyncRate, false, alternativeRefreshRates, supportedHdrTypes);
+ vsyncRate, /* isSynthetic= */ false, alternativeRefreshRates, supportedHdrTypes);
}
public interface Listener {
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index baa154d..184ae41 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -47,6 +47,8 @@
private final BrightnessEvent mBrightnessEvent;
private final int mBrightnessAdjustmentFlag;
+ private final boolean mIsUserInitiatedChange;
+
private DisplayBrightnessState(Builder builder) {
mBrightness = builder.getBrightness();
mSdrBrightness = builder.getSdrBrightness();
@@ -60,6 +62,7 @@
mShouldUpdateScreenBrightnessSetting = builder.shouldUpdateScreenBrightnessSetting();
mBrightnessEvent = builder.getBrightnessEvent();
mBrightnessAdjustmentFlag = builder.getBrightnessAdjustmentFlag();
+ mIsUserInitiatedChange = builder.isUserInitiatedChange();
}
/**
@@ -148,6 +151,13 @@
return mBrightnessAdjustmentFlag;
}
+ /**
+ * Gets if the current brightness changes are because of a user initiated change
+ */
+ public boolean isUserInitiatedChange() {
+ return mIsUserInitiatedChange;
+ }
+
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder("DisplayBrightnessState:");
@@ -168,6 +178,7 @@
stringBuilder.append("\n mBrightnessEvent:")
.append(Objects.toString(mBrightnessEvent, "null"));
stringBuilder.append("\n mBrightnessAdjustmentFlag:").append(mBrightnessAdjustmentFlag);
+ stringBuilder.append("\n mIsUserInitiatedChange:").append(mIsUserInitiatedChange);
return stringBuilder.toString();
}
@@ -199,7 +210,8 @@
&& mShouldUpdateScreenBrightnessSetting
== otherState.shouldUpdateScreenBrightnessSetting()
&& Objects.equals(mBrightnessEvent, otherState.getBrightnessEvent())
- && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag();
+ && mBrightnessAdjustmentFlag == otherState.getBrightnessAdjustmentFlag()
+ && mIsUserInitiatedChange == otherState.isUserInitiatedChange();
}
@Override
@@ -207,7 +219,8 @@
return Objects.hash(mBrightness, mSdrBrightness, mBrightnessReason,
mShouldUseAutoBrightness, mIsSlowChange, mMaxBrightness, mMinBrightness,
mCustomAnimationRate,
- mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag);
+ mShouldUpdateScreenBrightnessSetting, mBrightnessEvent, mBrightnessAdjustmentFlag,
+ mIsUserInitiatedChange);
}
/**
@@ -236,6 +249,8 @@
public int mBrightnessAdjustmentFlag = 0;
+ private boolean mIsUserInitiatedChange;
+
/**
* Create a builder starting with the values from the specified {@link
* DisplayBrightnessState}.
@@ -257,6 +272,7 @@
state.shouldUpdateScreenBrightnessSetting());
builder.setBrightnessEvent(state.getBrightnessEvent());
builder.setBrightnessAdjustmentFlag(state.getBrightnessAdjustmentFlag());
+ builder.setIsUserInitiatedChange(state.isUserInitiatedChange());
return builder;
}
@@ -464,5 +480,20 @@
mBrightnessAdjustmentFlag = brightnessAdjustmentFlag;
return this;
}
+
+ /**
+ * Gets if the current change is a user initiated change
+ */
+ public boolean isUserInitiatedChange() {
+ return mIsUserInitiatedChange;
+ }
+
+ /**
+ * This is used to set if the current change is a user initiated change
+ */
+ public Builder setIsUserInitiatedChange(boolean isUserInitiatedChange) {
+ mIsUserInitiatedChange = isUserInitiatedChange;
+ return this;
+ }
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index d4c0b01..5fd0253 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -49,6 +49,8 @@
import static android.provider.Settings.Secure.RESOLUTION_MODE_HIGH;
import static android.provider.Settings.Secure.RESOLUTION_MODE_UNKNOWN;
+import static com.android.server.display.layout.Layout.Display.POSITION_REAR;
+
import android.Manifest;
import android.annotation.EnforcePermission;
import android.annotation.NonNull;
@@ -4972,8 +4974,9 @@
}
final DisplayDevice displayDevice = display.getPrimaryDisplayDeviceLocked();
- final boolean ownContent = (displayDevice.getDisplayDeviceInfoLocked().flags
- & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
+ final boolean isRearDisplay = display.getDevicePositionLocked() == POSITION_REAR;
+ final boolean ownContent = ((displayDevice.getDisplayDeviceInfoLocked().flags
+ & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0) || isRearDisplay;
// If the display has enabled mirroring, but specified that it will be managed by
// WindowManager, return an invalid display id. This is to ensure we don't
// accidentally select the display id to mirror based on DM logic and instead allow
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index b97053b..8d71c70 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -1391,6 +1391,7 @@
// request changes.
final boolean wasShortTermModelActive =
mAutomaticBrightnessStrategy.isShortTermModelActive();
+ boolean userInitiatedChange = displayBrightnessState.isUserInitiatedChange();
boolean allowAutoBrightnessWhileDozing =
mDisplayBrightnessController.isAllowAutoBrightnessWhileDozingConfig();
if (mFlags.offloadControlsDozeAutoBrightness() && mFlags.isDisplayOffloadEnabled()
@@ -1413,13 +1414,14 @@
mPowerRequest.policy,
mDisplayBrightnessController.getLastUserSetScreenBrightness(),
userSetBrightnessChanged);
+
+ // If the brightness is already set then it's been overridden by something other than
+ // the user, or is a temporary adjustment.
+ userInitiatedChange = (Float.isNaN(brightnessState))
+ && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
+ || userSetBrightnessChanged);
}
- // If the brightness is already set then it's been overridden by something other than the
- // user, or is a temporary adjustment.
- boolean userInitiatedChange = (Float.isNaN(brightnessState))
- && (mAutomaticBrightnessStrategy.getAutoBrightnessAdjustmentChanged()
- || userSetBrightnessChanged);
final int autoBrightnessState = mAutomaticBrightnessStrategy.isAutoBrightnessEnabled()
? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
diff --git a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
index aa17df6..d567331 100644
--- a/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
+++ b/services/core/java/com/android/server/display/brightness/DisplayBrightnessController.java
@@ -600,6 +600,7 @@
private StrategyExecutionRequest constructStrategyExecutionRequest(
DisplayManagerInternal.DisplayPowerRequest displayPowerRequest) {
float currentScreenBrightness = getCurrentBrightness();
- return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness);
+ return new StrategyExecutionRequest(displayPowerRequest, currentScreenBrightness,
+ mUserSetScreenBrightnessUpdated);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
index 82c0bbf..304640b 100644
--- a/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
+++ b/services/core/java/com/android/server/display/brightness/StrategyExecutionRequest.java
@@ -29,10 +29,14 @@
private final float mCurrentScreenBrightness;
+ // Represents if the user set screen brightness was changed or not.
+ private boolean mUserSetBrightnessChanged;
+
public StrategyExecutionRequest(DisplayManagerInternal.DisplayPowerRequest displayPowerRequest,
- float currentScreenBrightness) {
+ float currentScreenBrightness, boolean userSetBrightnessChanged) {
mDisplayPowerRequest = displayPowerRequest;
mCurrentScreenBrightness = currentScreenBrightness;
+ mUserSetBrightnessChanged = userSetBrightnessChanged;
}
public DisplayManagerInternal.DisplayPowerRequest getDisplayPowerRequest() {
@@ -43,6 +47,10 @@
return mCurrentScreenBrightness;
}
+ public boolean isUserSetBrightnessChanged() {
+ return mUserSetBrightnessChanged;
+ }
+
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StrategyExecutionRequest)) {
@@ -50,11 +58,13 @@
}
StrategyExecutionRequest other = (StrategyExecutionRequest) obj;
return Objects.equals(mDisplayPowerRequest, other.getDisplayPowerRequest())
- && mCurrentScreenBrightness == other.getCurrentScreenBrightness();
+ && mCurrentScreenBrightness == other.getCurrentScreenBrightness()
+ && mUserSetBrightnessChanged == other.isUserSetBrightnessChanged();
}
@Override
public int hashCode() {
- return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness);
+ return Objects.hash(mDisplayPowerRequest, mCurrentScreenBrightness,
+ mUserSetBrightnessChanged);
}
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
index 2907364..2b5241f 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategy.java
@@ -290,6 +290,8 @@
.setBrightnessAdjustmentFlag(mAutoBrightnessAdjustmentReasonsFlags)
.setShouldUpdateScreenBrightnessSetting(
brightness != strategyExecutionRequest.getCurrentScreenBrightness())
+ .setIsUserInitiatedChange(getAutoBrightnessAdjustmentChanged()
+ || strategyExecutionRequest.isUserSetBrightnessChanged())
.build();
}
diff --git a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
index 3463649a..0b92317 100644
--- a/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
+++ b/services/core/java/com/android/server/display/brightness/strategy/FallbackBrightnessStrategy.java
@@ -45,6 +45,7 @@
// The fallback brightness might change due to clamping. Make sure we tell the rest
// of the system by updating the setting
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(strategyExecutionRequest.isUserSetBrightnessChanged())
.build();
}
diff --git a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
index f923cbc9..f56d803 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -353,7 +353,7 @@
}
/**
- * @return Whether to ignore preferredRefreshRate app request or not
+ * @return Whether to ignore preferredRefreshRate app request conversion to display mode or not
*/
public boolean ignoreAppPreferredRefreshRateRequest() {
return mIgnoreAppPreferredRefreshRate.isEnabled();
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 76a2827..d519748 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -1254,13 +1254,9 @@
* Responsible for keeping track of app requested refresh rates per display
*/
public final class AppRequestObserver {
- private final SparseArray<Display.Mode> mAppRequestedModeByDisplay;
- private final SparseArray<RefreshRateRange> mAppPreferredRefreshRateRangeByDisplay;
private final boolean mIgnorePreferredRefreshRate;
AppRequestObserver(DisplayManagerFlags flags) {
- mAppRequestedModeByDisplay = new SparseArray<>();
- mAppPreferredRefreshRateRangeByDisplay = new SparseArray<>();
mIgnorePreferredRefreshRate = flags.ignoreAppPreferredRefreshRateRequest();
}
@@ -1269,34 +1265,77 @@
*/
public void setAppRequest(int displayId, int modeId, float requestedRefreshRate,
float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
+ Display.Mode requestedMode;
+ synchronized (mLock) {
+ requestedMode = findModeLocked(displayId, modeId, requestedRefreshRate);
+ }
- if (modeId == 0 && requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) {
+ Vote frameRateVote = getFrameRateVote(
+ requestedMinRefreshRateRange, requestedMaxRefreshRateRange);
+ Vote baseModeRefreshRateVote = getBaseModeVote(requestedMode, requestedRefreshRate);
+ Vote sizeVote = getSizeVote(requestedMode);
+
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
+ frameRateVote);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
+ baseModeRefreshRateVote);
+ mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
+ }
+
+ private Display.Mode findModeLocked(int displayId, int modeId, float requestedRefreshRate) {
+ Display.Mode mode = null;
+ if (modeId != 0) {
+ mode = findAppModeByIdLocked(displayId, modeId);
+ } else if (requestedRefreshRate != 0 && !mIgnorePreferredRefreshRate) { // modeId == 0
// Scan supported modes returned to find a mode with the same
// size as the default display mode but with the specified refresh rate instead.
- Display.Mode mode = findDefaultModeByRefreshRate(displayId, requestedRefreshRate);
- if (mode != null) {
- modeId = mode.getModeId();
- } else {
+ mode = findDefaultModeByRefreshRateLocked(displayId, requestedRefreshRate);
+ if (mode == null) {
Slog.e(TAG, "Couldn't find a mode for the requestedRefreshRate: "
+ requestedRefreshRate + " on Display: " + displayId);
}
}
+ return mode;
+ }
- synchronized (mLock) {
- setAppRequestedModeLocked(displayId, modeId);
- setAppPreferredRefreshRateRangeLocked(displayId, requestedMinRefreshRateRange,
- requestedMaxRefreshRateRange);
+ private Vote getFrameRateVote(float minRefreshRate, float maxRefreshRate) {
+ RefreshRateRange refreshRateRange = null;
+ if (minRefreshRate > 0 || maxRefreshRate > 0) {
+ float max = maxRefreshRate > 0
+ ? maxRefreshRate : Float.POSITIVE_INFINITY;
+ refreshRateRange = new RefreshRateRange(minRefreshRate, max);
+ if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
+ // minRefreshRate/maxRefreshRate were invalid
+ refreshRateRange = null;
+ }
}
+ return refreshRateRange != null
+ ? Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max) : null;
+ }
+
+ private Vote getSizeVote(@Nullable Display.Mode mode) {
+ return mode != null
+ ? Vote.forSize(mode.getPhysicalWidth(), mode.getPhysicalHeight()) : null;
+ }
+
+ private Vote getBaseModeVote(@Nullable Display.Mode mode, float requestedRefreshRate) {
+ Vote vote = null;
+ if (mode != null) {
+ if (mode.isSynthetic()) {
+ vote = Vote.forRequestedRefreshRate(mode.getRefreshRate());
+ } else {
+ vote = Vote.forBaseModeRefreshRate(mode.getRefreshRate());
+ }
+ } else if (requestedRefreshRate != 0f && mIgnorePreferredRefreshRate) {
+ vote = Vote.forRequestedRefreshRate(requestedRefreshRate);
+ } // !mIgnorePreferredRefreshRate case is handled by findModeLocked
+ return vote;
}
@Nullable
- private Display.Mode findDefaultModeByRefreshRate(int displayId, float refreshRate) {
- Display.Mode[] modes;
- Display.Mode defaultMode;
- synchronized (mLock) {
- modes = mAppSupportedModesByDisplay.get(displayId);
- defaultMode = mDefaultModeByDisplay.get(displayId);
- }
+ private Display.Mode findDefaultModeByRefreshRateLocked(int displayId, float refreshRate) {
+ Display.Mode[] modes = mAppSupportedModesByDisplay.get(displayId);
+ Display.Mode defaultMode = mDefaultModeByDisplay.get(displayId);
for (int i = 0; i < modes.length; i++) {
if (modes[i].matches(defaultMode.getPhysicalWidth(),
defaultMode.getPhysicalHeight(), refreshRate)) {
@@ -1306,69 +1345,6 @@
return null;
}
- private void setAppRequestedModeLocked(int displayId, int modeId) {
- final Display.Mode requestedMode = findAppModeByIdLocked(displayId, modeId);
- if (Objects.equals(requestedMode, mAppRequestedModeByDisplay.get(displayId))) {
- return;
- }
- final Vote baseModeRefreshRateVote;
- final Vote sizeVote;
- if (requestedMode != null) {
- mAppRequestedModeByDisplay.put(displayId, requestedMode);
- sizeVote = Vote.forSize(requestedMode.getPhysicalWidth(),
- requestedMode.getPhysicalHeight());
- if (requestedMode.isSynthetic()) {
- // TODO: for synthetic mode we should not limit frame rate, we must ensure
- // that frame rate is reachable within other Votes constraints
- baseModeRefreshRateVote = Vote.forRenderFrameRates(
- requestedMode.getRefreshRate(), requestedMode.getRefreshRate());
- } else {
- baseModeRefreshRateVote =
- Vote.forBaseModeRefreshRate(requestedMode.getRefreshRate());
- }
- } else {
- mAppRequestedModeByDisplay.remove(displayId);
- baseModeRefreshRateVote = null;
- sizeVote = null;
- }
-
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE,
- baseModeRefreshRateVote);
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote);
- }
-
- private void setAppPreferredRefreshRateRangeLocked(int displayId,
- float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) {
- final Vote vote;
-
- RefreshRateRange refreshRateRange = null;
- if (requestedMinRefreshRateRange > 0 || requestedMaxRefreshRateRange > 0) {
- float min = requestedMinRefreshRateRange;
- float max = requestedMaxRefreshRateRange > 0
- ? requestedMaxRefreshRateRange : Float.POSITIVE_INFINITY;
- refreshRateRange = new RefreshRateRange(min, max);
- if (refreshRateRange.min == 0 && refreshRateRange.max == 0) {
- // requestedMinRefreshRateRange/requestedMaxRefreshRateRange were invalid
- refreshRateRange = null;
- }
- }
-
- if (Objects.equals(refreshRateRange,
- mAppPreferredRefreshRateRangeByDisplay.get(displayId))) {
- return;
- }
-
- if (refreshRateRange != null) {
- mAppPreferredRefreshRateRangeByDisplay.put(displayId, refreshRateRange);
- vote = Vote.forRenderFrameRates(refreshRateRange.min, refreshRateRange.max);
- } else {
- mAppPreferredRefreshRateRangeByDisplay.remove(displayId);
- vote = null;
- }
- mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE,
- vote);
- }
-
private Display.Mode findAppModeByIdLocked(int displayId, int modeId) {
Display.Mode[] modes = mAppSupportedModesByDisplay.get(displayId);
if (modes == null) {
@@ -1384,19 +1360,7 @@
private void dumpLocked(PrintWriter pw) {
pw.println(" AppRequestObserver");
- pw.println(" mAppRequestedModeByDisplay:");
- for (int i = 0; i < mAppRequestedModeByDisplay.size(); i++) {
- final int id = mAppRequestedModeByDisplay.keyAt(i);
- final Display.Mode mode = mAppRequestedModeByDisplay.valueAt(i);
- pw.println(" " + id + " -> " + mode);
- }
- pw.println(" mAppPreferredRefreshRateRangeByDisplay:");
- for (int i = 0; i < mAppPreferredRefreshRateRangeByDisplay.size(); i++) {
- final int id = mAppPreferredRefreshRateRangeByDisplay.keyAt(i);
- final RefreshRateRange refreshRateRange =
- mAppPreferredRefreshRateRangeByDisplay.valueAt(i);
- pw.println(" " + id + " -> " + refreshRateRange);
- }
+ pw.println(" mIgnorePreferredRefreshRate: " + mIgnorePreferredRefreshRate);
}
}
diff --git a/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java b/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java
new file mode 100644
index 0000000..843cf88
--- /dev/null
+++ b/services/core/java/com/android/server/display/mode/RequestedRefreshRateVote.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode;
+
+import android.annotation.NonNull;
+
+import java.util.Objects;
+
+class RequestedRefreshRateVote implements Vote {
+ final float mRefreshRate;
+
+ RequestedRefreshRateVote(float refreshRate) {
+ this.mRefreshRate = refreshRate;
+ }
+
+ @Override
+ public void updateSummary(@NonNull VoteSummary summary) {
+ summary.requestedRefreshRates.add(mRefreshRate);
+ }
+
+ @Override
+ public String toString() {
+ return "RequestedRefreshRateVote{ refreshRate=" + mRefreshRate + " }";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof RequestedRefreshRateVote that)) return false;
+ return Float.compare(mRefreshRate, that.mRefreshRate) == 0;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mRefreshRate);
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
index 5b6bbc5..a83b939 100644
--- a/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
+++ b/services/core/java/com/android/server/display/mode/SyntheticModeManager.java
@@ -32,7 +32,9 @@
*/
public class SyntheticModeManager {
private static final float FLOAT_TOLERANCE = 0.01f;
- private static final float SYNTHETIC_MODE_HIGH_BOUNDARY = 60f + FLOAT_TOLERANCE;
+ private static final float SYNTHETIC_MODE_REFRESH_RATE = 60f;
+ private static final float SYNTHETIC_MODE_HIGH_BOUNDARY =
+ SYNTHETIC_MODE_REFRESH_RATE + FLOAT_TOLERANCE;
private final boolean mSynthetic60HzModesEnabled;
@@ -62,7 +64,7 @@
nextModeId = mode.getModeId();
}
- float divisor = mode.getVsyncRate() / 60f;
+ float divisor = mode.getVsyncRate() / SYNTHETIC_MODE_REFRESH_RATE;
boolean is60HzAchievable = Math.abs(divisor - Math.round(divisor)) < FLOAT_TOLERANCE;
if (is60HzAchievable) {
sizes.put(new Size(mode.getPhysicalWidth(), mode.getPhysicalHeight()),
diff --git a/services/core/java/com/android/server/display/mode/Vote.java b/services/core/java/com/android/server/display/mode/Vote.java
index 3e8b3c5..1ec469c 100644
--- a/services/core/java/com/android/server/display/mode/Vote.java
+++ b/services/core/java/com/android/server/display/mode/Vote.java
@@ -186,6 +186,10 @@
return new BaseModeRefreshRateVote(baseModeRefreshRate);
}
+ static Vote forRequestedRefreshRate(float refreshRate) {
+ return new RequestedRefreshRateVote(refreshRate);
+ }
+
static Vote forSupportedRefreshRates(List<SupportedModeData> supportedModes) {
if (supportedModes.isEmpty()) {
return null;
diff --git a/services/core/java/com/android/server/display/mode/VoteSummary.java b/services/core/java/com/android/server/display/mode/VoteSummary.java
index d4ce892..00a9226 100644
--- a/services/core/java/com/android/server/display/mode/VoteSummary.java
+++ b/services/core/java/com/android/server/display/mode/VoteSummary.java
@@ -23,7 +23,9 @@
import android.view.SurfaceControl;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
final class VoteSummary {
private static final float FLOAT_TOLERANCE = SurfaceControl.RefreshRateRange.FLOAT_TOLERANCE;
@@ -38,7 +40,14 @@
public int minWidth;
public int minHeight;
public boolean disableRefreshRateSwitching;
+ /**
+ * available modes should have mode with specific refresh rate
+ */
public float appRequestBaseModeRefreshRate;
+ /**
+ * requestRefreshRate should be within [minRenderFrameRate, maxRenderFrameRate] range
+ */
+ public Set<Float> requestedRefreshRates = new HashSet<>();
@Nullable
public List<SupportedRefreshRatesVote.RefreshRates> supportedRefreshRates;
@@ -329,6 +338,21 @@
}
return false;
}
+
+ for (Float requestedRefreshRate : requestedRefreshRates) {
+ if (requestedRefreshRate < minRenderFrameRate
+ || requestedRefreshRate > maxRenderFrameRate) {
+ if (mLoggingEnabled) {
+ Slog.w(TAG, "Requested refreshRate is outside frame rate range"
+ + ": requestedRefreshRates=" + requestedRefreshRates
+ + ", requestedRefreshRate=" + requestedRefreshRate
+ + ", minRenderFrameRate=" + minRenderFrameRate
+ + ", maxRenderFrameRate=" + maxRenderFrameRate);
+ }
+ return false;
+ }
+ }
+
return true;
}
@@ -370,6 +394,7 @@
minHeight = 0;
disableRefreshRateSwitching = false;
appRequestBaseModeRefreshRate = 0f;
+ requestedRefreshRates.clear();
supportedRefreshRates = null;
supportedModeIds = null;
if (mLoggingEnabled) {
@@ -393,6 +418,7 @@
+ ", minHeight=" + minHeight
+ ", disableRefreshRateSwitching=" + disableRefreshRateSwitching
+ ", appRequestBaseModeRefreshRate=" + appRequestBaseModeRefreshRate
+ + ", requestRefreshRates=" + requestedRefreshRates
+ ", supportedRefreshRates=" + supportedRefreshRates
+ ", supportedModeIds=" + supportedModeIds
+ ", mIsDisplayResolutionRangeVotingEnabled="
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d4bbe84..61022cc 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1346,10 +1346,13 @@
// In a multi-resumed environment, like in a freeform device, the top
// activity can be resumed, but it might not be the focused app.
- // Set focused app when top activity is resumed
- if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null
- && taskDisplayArea.mDisplayContent.mFocusedApp != next) {
- taskDisplayArea.mDisplayContent.setFocusedApp(next);
+ // Set focused app when top activity is resumed. However, we shouldn't do it for a
+ // same task because it can break focused state. (e.g. activity embedding)
+ if (taskDisplayArea.inMultiWindowMode() && taskDisplayArea.mDisplayContent != null) {
+ final ActivityRecord focusedApp = taskDisplayArea.mDisplayContent.mFocusedApp;
+ if (focusedApp == null || focusedApp.getTask() != next.getTask()) {
+ taskDisplayArea.mDisplayContent.setFocusedApp(next);
+ }
}
ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivity: Top activity "
+ "resumed %s", next);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index 7483b43..c7fd979 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -202,9 +202,9 @@
private String getDefaultSmsPackage() {
//TODO(b/319449037): Unflag the following change.
if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) {
- return SmsApplication.getDefaultSmsApplicationAsUser(
- mContext, /*updateIfNeeded=*/ false, mContext.getUser())
- .getPackageName();
+ ComponentName defaultSmsApp = SmsApplication.getDefaultSmsApplicationAsUser(
+ mContext, /*updateIfNeeded=*/ false, mContext.getUser());
+ return defaultSmsApp != null ? defaultSmsApp.getPackageName() : null;
} else {
return Telephony.Sms.getDefaultSmsPackage(mContext);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
index 8db896b..bba4c8d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayBrightnessStateTest.java
@@ -58,6 +58,7 @@
.setShouldUseAutoBrightness(shouldUseAutoBrightness)
.setShouldUpdateScreenBrightnessSetting(shouldUpdateScreenBrightnessSetting)
.setBrightnessAdjustmentFlag(brightnessAdjustmentFlag)
+ .setIsUserInitiatedChange(true)
.build();
assertEquals(displayBrightnessState.getBrightness(), brightness, FLOAT_DELTA);
@@ -109,7 +110,9 @@
.append("\n mBrightnessEvent:")
.append(Objects.toString(displayBrightnessState.getBrightnessEvent(), "null"))
.append("\n mBrightnessAdjustmentFlag:")
- .append(displayBrightnessState.getBrightnessAdjustmentFlag());
+ .append(displayBrightnessState.getBrightnessAdjustmentFlag())
+ .append("\n mIsUserInitiatedChange:")
+ .append(displayBrightnessState.isUserInitiatedChange());
return sb.toString();
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
index c5105e7..323ef6a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
@@ -121,7 +121,8 @@
any(StrategySelectionRequest.class))).thenReturn(displayBrightnessStrategy);
mDisplayBrightnessController.updateBrightness(displayPowerRequest, targetDisplayState);
verify(displayBrightnessStrategy).updateBrightness(
- eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS)));
+ eq(new StrategyExecutionRequest(displayPowerRequest, DEFAULT_BRIGHTNESS,
+ /* userSetBrightnessChanged= */ false)));
assertEquals(mDisplayBrightnessController.getCurrentDisplayBrightnessStrategy(),
displayBrightnessStrategy);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
index 7a6a911..4d5ff55 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutoBrightnessFallbackStrategyTest.java
@@ -115,7 +115,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mAutoBrightnessFallbackStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
index afb5a5c..8a33f34 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
@@ -513,9 +513,11 @@
.setBrightnessEvent(brightnessEvent)
.setBrightnessAdjustmentFlag(BrightnessReason.ADJUSTMENT_AUTO)
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
- .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f));
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
@@ -560,9 +562,11 @@
.setBrightnessEvent(brightnessEvent)
.setBrightnessAdjustmentFlag(BrightnessReason.ADJUSTMENT_AUTO_TEMP)
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
.build();
DisplayBrightnessState actualDisplayBrightnessState = mAutomaticBrightnessStrategy
- .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f));
+ .updateBrightness(new StrategyExecutionRequest(displayPowerRequest, 0.6f,
+ /* userSetBrightnessChanged= */ true));
assertEquals(expectedDisplayBrightnessState, actualDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
index 47f1746..3534325 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mBoostBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
index 9246780..bd6d8be 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
@@ -57,7 +57,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mDozeBrightnessModeStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
index c4767ae..4cae35d 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FallbackBrightnessStrategyTest.java
@@ -57,10 +57,12 @@
.setSdrBrightness(currentBrightness)
.setDisplayBrightnessStrategyName(mFallbackBrightnessStrategy.getName())
.setShouldUpdateScreenBrightnessSetting(true)
+ .setIsUserInitiatedChange(true)
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mFallbackBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, currentBrightness));
+ new StrategyExecutionRequest(displayPowerRequest, currentBrightness,
+ /* userSetBrightnessChanged= */ true));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
index 682c9cc..fdaf8f6 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
@@ -61,7 +61,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mFollowerBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(expectedDisplayBrightnessState, updatedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
index ccf6309..e3c2760 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OffloadBrightnessStrategyTest.java
@@ -72,7 +72,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mOffloadBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
assertEquals(PowerManager.BRIGHTNESS_INVALID_FLOAT, mOffloadBrightnessStrategy
.getOffloadScreenBrightness(), 0.0f);
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
index 8e7b463..ebe407b 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mOverrideBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
index e799d0e..4bad569 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
@@ -58,7 +58,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mScreenOffBrightnessModeStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
index aaada41..5410a20 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
@@ -60,7 +60,8 @@
.build();
DisplayBrightnessState updatedDisplayBrightnessState =
mTemporaryBrightnessStrategy.updateBrightness(
- new StrategyExecutionRequest(displayPowerRequest, 0.2f));
+ new StrategyExecutionRequest(displayPowerRequest, 0.2f,
+ /* userSetBrightnessChanged= */ false));
assertEquals(updatedDisplayBrightnessState, expectedDisplayBrightnessState);
}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
index cf6146f..34c6ba9 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt
@@ -117,11 +117,11 @@
BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), RenderVote(60f, 90f)),
PREFERRED_REFRESH_RATE(false, 0, 60f, 0f, 0f,
BaseModeRefreshRateVote(60f), SizeVote(1000, 1000, 1000, 1000), null),
- PREFERRED_REFRESH_RATE_IGNORED(true, 0, 60f, 0f, 0f,
- null, null, null),
+ PREFERRED_REFRESH_RATE_IGNORE_BASE_MODE_CONVERSION(true, 0, 60f, 0f, 0f,
+ RequestedRefreshRateVote(60f), null, null),
PREFERRED_REFRESH_RATE_INVALID(false, 0, 25f, 0f, 0f,
null, null, null),
SYNTHETIC_MODE(false, 99, 0f, 0f, 0f,
- RenderVote(45f, 45f), SizeVote(1000, 1000, 1000, 1000), null),
+ RequestedRefreshRateVote(45f), SizeVote(1000, 1000, 1000, 1000), null),
}
}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
new file mode 100644
index 0000000..dbe9e4a
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/RequestedRefreshRateVoteTest.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.mode
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class RequestedRefreshRateVoteTest {
+
+ @Test
+ fun `updates requestedRefreshRates`() {
+ val refreshRate = 90f
+ val vote = RequestedRefreshRateVote(refreshRate)
+ val summary = createVotesSummary()
+
+ vote.updateSummary(summary)
+
+ assertThat(summary.requestedRefreshRates).hasSize(1)
+ assertThat(summary.requestedRefreshRates).contains(refreshRate)
+ }
+
+ @Test
+ fun `updates requestedRefreshRates with multiple refresh rates`() {
+ val refreshRate1 = 90f
+ val vote1 = RequestedRefreshRateVote(refreshRate1)
+
+ val refreshRate2 = 60f
+ val vote2 = RequestedRefreshRateVote(refreshRate2)
+
+ val summary = createVotesSummary()
+
+ vote1.updateSummary(summary)
+ vote2.updateSummary(summary)
+
+ assertThat(summary.requestedRefreshRates).hasSize(2)
+ assertThat(summary.requestedRefreshRates).containsExactly(refreshRate1, refreshRate2)
+ }
+}
\ No newline at end of file
diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
index 5da1bb6..dd5e1be 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/mode/VoteSummaryTest.kt
@@ -152,13 +152,51 @@
assertThat(result.map { it.modeId }).containsExactlyElementsIn(testCase.expectedModeIds)
}
+
+ @Test
+ fun `summary invalid if has requestedRefreshRate less than minRenederRate`() {
+ val summary = createSummary()
+ summary.requestedRefreshRates = setOf(30f, 90f)
+ summary.minRenderFrameRate = 60f
+ summary.maxRenderFrameRate = 120f
+
+ val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+ assertThat(result).isEmpty()
+ }
+
+ @Test
+ fun `summary invalid if has requestedRefreshRate more than maxRenderFrameRate`() {
+ val summary = createSummary()
+ summary.requestedRefreshRates = setOf(60f, 240f)
+ summary.minRenderFrameRate = 60f
+ summary.maxRenderFrameRate = 120f
+
+ val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+ assertThat(result).isEmpty()
+ }
+
+ @Test
+ fun `summary valid if all requestedRefreshRates inside render rate limits`() {
+ val summary = createSummary()
+ summary.requestedRefreshRates = setOf(60f, 90f)
+ summary.minRenderFrameRate = 60f
+ summary.maxRenderFrameRate = 120f
+
+ val result = summary.filterModes(arrayOf(createMode(1, 90f, 90f)))
+
+ assertThat(result).hasSize(1)
+ }
}
+
+
private fun createMode(modeId: Int, refreshRate: Float, vsyncRate: Float): Display.Mode {
return Display.Mode(modeId, 600, 800, refreshRate, vsyncRate, false,
FloatArray(0), IntArray(0))
}
-private fun createSummary(supportedModesVoteEnabled: Boolean): VoteSummary {
+private fun createSummary(supportedModesVoteEnabled: Boolean = false): VoteSummary {
val summary = createVotesSummary(supportedModesVoteEnabled = supportedModesVoteEnabled)
summary.width = 600
summary.height = 800