Merge "SeekBarVolumizer: Use TAG for sample sound" into main
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 646e05c..9961c4f 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1791,7 +1791,10 @@
* <ol>
* <li>Run as soon as possible</li>
* <li>Be less restricted during Doze and battery saver</li>
- * <li>Bypass Doze, app standby, and battery saver network restrictions</li>
+ * <li>
+ * Bypass Doze, app standby, and battery saver network restrictions (if the job
+ * has a {@link #setRequiredNetwork(NetworkRequest) connectivity constraint})
+ * </li>
* <li>Be less likely to be killed than regular jobs</li>
* <li>Be subject to background location throttling</li>
* <li>Be exempt from delay to optimize job execution</li>
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 23b36e2..bff4353 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3859,10 +3859,16 @@
// Only let the app use the higher runtime if it hasn't repeatedly timed out.
final String timeoutTag = job.shouldTreatAsExpeditedJob()
? QUOTA_TRACKER_TIMEOUT_EJ_TAG : QUOTA_TRACKER_TIMEOUT_REG_TAG;
+ // Developers are informed that expedited jobs can be stopped earlier than regular jobs
+ // and so shouldn't use them for long pieces of work. There's little reason to let
+ // them run longer than the normal 10 minutes.
+ final long normalUpperLimitMs = job.shouldTreatAsExpeditedJob()
+ ? mConstants.RUNTIME_MIN_GUARANTEE_MS
+ : mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
final long upperLimitMs =
mQuotaTracker.isWithinQuota(job.getTimeoutBlameUserId(),
job.getTimeoutBlamePackageName(), timeoutTag)
- ? mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS
+ ? normalUpperLimitMs
: mConstants.RUNTIME_MIN_GUARANTEE_MS;
return Math.min(upperLimitMs,
mConstants.USE_TARE_POLICY
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 2d49cfb..721a8bd 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -427,34 +427,30 @@
boolean binding = false;
boolean startedWithForegroundFlag = false;
try {
- final Context.BindServiceFlags bindFlags;
+ long bindFlags = Context.BIND_AUTO_CREATE | Context.BIND_NOT_APP_COMPONENT_USAGE;
if (job.shouldTreatAsUserInitiatedJob() && !job.isUserBgRestricted()) {
// If the user has bg restricted the app, don't give the job FG privileges
// such as bypassing data saver or getting the higher foreground proc state.
// If we've gotten to this point, the app is most likely in the foreground,
// so the job will run just fine while the user keeps the app in the foreground.
- bindFlags = Context.BindServiceFlags.of(
- Context.BIND_AUTO_CREATE
- | Context.BIND_ALMOST_PERCEPTIBLE
- | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
- | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS
- | Context.BIND_NOT_APP_COMPONENT_USAGE);
+ bindFlags |= Context.BIND_ALMOST_PERCEPTIBLE;
+ if (job.hasConnectivityConstraint()) {
+ // Only add network restriction bypass flags if the job requires network.
+ bindFlags |= Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
+ | Context.BIND_BYPASS_USER_NETWORK_RESTRICTIONS;
+ }
startedWithForegroundFlag = true;
} else if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
- bindFlags = Context.BindServiceFlags.of(
- Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALMOST_PERCEPTIBLE
- | Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS
- | Context.BIND_NOT_APP_COMPONENT_USAGE);
+ bindFlags |= Context.BIND_NOT_FOREGROUND | Context.BIND_ALMOST_PERCEPTIBLE;
+ if (job.hasConnectivityConstraint()) {
+ // Only add network restriction bypass flags if the job requires network.
+ bindFlags |= Context.BIND_BYPASS_POWER_NETWORK_RESTRICTIONS;
+ }
} else {
- bindFlags = Context.BindServiceFlags.of(
- Context.BIND_AUTO_CREATE
- | Context.BIND_NOT_FOREGROUND
- | Context.BIND_NOT_PERCEPTIBLE
- | Context.BIND_NOT_APP_COMPONENT_USAGE);
+ bindFlags |= Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_PERCEPTIBLE;
}
- binding = mContext.bindServiceAsUser(intent, this, bindFlags,
+ binding = mContext.bindServiceAsUser(intent, this,
+ Context.BindServiceFlags.of(bindFlags),
UserHandle.of(job.getUserId()));
} catch (SecurityException e) {
// Some permission policy, for example INTERACT_ACROSS_USERS and
diff --git a/core/api/current.txt b/core/api/current.txt
index 1b0311d..013010b 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -18633,6 +18633,7 @@
method @Deprecated @Nullable public android.security.identity.IdentityCredential getIdentityCredential();
method @FlaggedApi("android.hardware.biometrics.add_key_agreement_crypto_object") @Nullable public javax.crypto.KeyAgreement getKeyAgreement();
method @Nullable public javax.crypto.Mac getMac();
+ method @FlaggedApi("android.hardware.biometrics.get_op_id_crypto_object") public long getOpId();
method @Nullable public android.security.identity.PresentationSession getPresentationSession();
method @Nullable public java.security.Signature getSignature();
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 814be2f..346d62b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4090,7 +4090,7 @@
field public static final int PROTECTION_FLAG_MODULE = 4194304; // 0x400000
field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
field public static final int PROTECTION_FLAG_RECENTS = 33554432; // 0x2000000
- field public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
+ field @Deprecated public static final int PROTECTION_FLAG_RETAIL_DEMO = 16777216; // 0x1000000
field public static final int PROTECTION_FLAG_ROLE = 67108864; // 0x4000000
field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 10747bb..357ee0a 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -66,6 +66,7 @@
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.TimeoutException;
/**
@@ -1890,6 +1891,7 @@
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options) {
+ Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
@@ -1969,6 +1971,10 @@
public int execStartActivitiesAsUser(Context who, IBinder contextThread,
IBinder token, Activity target, Intent[] intents, Bundle options,
int userId) {
+ Objects.requireNonNull(intents);
+ for (int i = intents.length - 1; i >= 0; i--) {
+ Objects.requireNonNull(intents[i]);
+ }
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
for (Intent intent : intents) {
@@ -2022,33 +2028,34 @@
* Like {@link #execStartActivity(android.content.Context, android.os.IBinder,
* android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)},
* but for calls from a {@link Fragment}.
- *
+ *
* @param who The Context from which the activity is being started.
* @param contextThread The main thread of the Context from which the activity
* is being started.
- * @param token Internal token identifying to the system who is starting
+ * @param token Internal token identifying to the system who is starting
* the activity; may be null.
- * @param target Which element is performing the start (and thus receiving
+ * @param target Which element is performing the start (and thus receiving
* any result).
* @param intent The actual Intent to start.
- * @param requestCode Identifier for this request's result; less than zero
+ * @param requestCode Identifier for this request's result; less than zero
* if the caller is not expecting a result.
- *
- * @return To force the return of a particular result, return an
+ *
+ * @return To force the return of a particular result, return an
* ActivityResult object containing the desired data; otherwise
* return null. The default implementation always returns null.
- *
+ *
* @throws android.content.ActivityNotFoundException
- *
+ *
* @see Activity#startActivity(Intent)
* @see Activity#startActivityForResult(Intent, int)
- *
+ *
* {@hide}
*/
@UnsupportedAppUsage
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
+ Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
adjustIntentForCtsInSdkSandboxInstrumentation(intent);
@@ -2123,6 +2130,7 @@
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String resultWho,
Intent intent, int requestCode, Bundle options, UserHandle user) {
+ Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
adjustIntentForCtsInSdkSandboxInstrumentation(intent);
@@ -2176,6 +2184,7 @@
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode, Bundle options,
boolean ignoreTargetSecurity, int userId) {
+ Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
adjustIntentForCtsInSdkSandboxInstrumentation(intent);
@@ -2230,6 +2239,7 @@
public void execStartActivityFromAppTask(
Context who, IBinder contextThread, IAppTask appTask,
Intent intent, Bundle options) {
+ Objects.requireNonNull(intent);
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (isSdkSandboxAllowedToStartActivities()) {
adjustIntentForCtsInSdkSandboxInstrumentation(intent);
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index cdda12e..012b6c4 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -273,6 +273,9 @@
* to the <code>retailDemo</code> value of
* {@link android.R.attr#protectionLevel}.
*
+ * @deprecated This flag has been replaced by the retail demo role and is a no-op since Android
+ * V.
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 7a43286..97bbfbb 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
+import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
@@ -851,6 +852,14 @@
public @Nullable KeyAgreement getKeyAgreement() {
return super.getKeyAgreement();
}
+
+ /**
+ * Get the operation handle associated with this object or 0 if none.
+ */
+ @FlaggedApi(FLAG_GET_OP_ID_CRYPTO_OBJECT)
+ public long getOpId() {
+ return super.getOpId();
+ }
}
/**
diff --git a/core/java/android/hardware/biometrics/CryptoObject.java b/core/java/android/hardware/biometrics/CryptoObject.java
index 39fbe83..8d3ea3f 100644
--- a/core/java/android/hardware/biometrics/CryptoObject.java
+++ b/core/java/android/hardware/biometrics/CryptoObject.java
@@ -154,7 +154,7 @@
* @hide
* @return the opId associated with this object or 0 if none
*/
- public final long getOpId() {
+ public long getOpId() {
if (mCrypto == null) {
return 0;
} else if (mCrypto instanceof IdentityCredential) {
diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig
index 979f570..56ec763 100644
--- a/core/java/android/hardware/biometrics/flags.aconfig
+++ b/core/java/android/hardware/biometrics/flags.aconfig
@@ -14,3 +14,10 @@
bug: "282058146"
}
+flag {
+ name: "get_op_id_crypto_object"
+ namespace: "biometrics"
+ description: "Feature flag for adding a get operation id api to CryptoObject."
+ bug: "307601768"
+}
+
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9f931b4..c012ff3 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -15279,6 +15279,7 @@
* max_history_files (int)
* max_history_buffer_kb (int)
* battery_charged_delay_ms (int)
+ * battery_charging_enforce_level (int)
* </pre>
*
* <p>
@@ -18306,8 +18307,11 @@
/**
* Delay for sending ACTION_CHARGING after device is plugged in.
- * This is used as an override for constants defined in BatteryStatsImpl for
- * ease of experimentation.
+ * This is used as an override for constants defined in BatteryStatsImpl. Its purposes are:
+ * <ol>
+ * <li>Ease of experimentation</li>
+ * <li>Customization of different device</li>
+ * </ol>
*
* @see com.android.internal.os.BatteryStatsImpl.Constants.KEY_BATTERY_CHARGED_DELAY_MS
* @hide
@@ -18317,6 +18321,22 @@
"battery_charging_state_update_delay";
/**
+ * Threshold battery level to enforce battery state as charging. That means when battery
+ * level is equal to or higher than this threshold, it is always considered charging, even
+ * if battery level lowered.
+ * This is used as an override for constants defined in BatteryStatsImpl. Its purposes are:
+ * <ol>
+ * <li>Ease of experimentation</li>
+ * <li>Customization of different device</li>
+ * </ol>
+ *
+ * @hide
+ * @see com.android.internal.os.BatteryStatsImpl.Constants.BATTERY_CHARGING_ENFORCE_LEVEL
+ */
+ public static final String BATTERY_CHARGING_STATE_ENFORCE_LEVEL =
+ "battery_charging_state_enforce_level";
+
+ /**
* A serialized string of params that will be loaded into a text classifier action model.
*
* @hide
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9d88af9..451a71b 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2671,7 +2671,7 @@
*
* @param minAlpha The min alpha the {@link SurfaceControl} is required to
* have to be considered inside the threshold.
- * @param minFractionRendered The min fraction of the SurfaceControl that was resented
+ * @param minFractionRendered The min fraction of the SurfaceControl that was presented
* to the user to be considered inside the threshold.
* @param stabilityRequirementMs The time in milliseconds required for the
* {@link SurfaceControl} to be in the threshold.
diff --git a/core/java/android/window/StartingWindowInfo.java b/core/java/android/window/StartingWindowInfo.java
index 451acbe..260d9a8 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -165,6 +165,13 @@
public static final int TYPE_PARAMETER_WINDOWLESS = 0x00000100;
/**
+ * Application has set Window_windowSplashScreenBehavior to
+ * SPLASH_SCREEN_BEHAVIOR_ICON_PREFERRED.
+ * @hide
+ */
+ public static final int TYPE_PARAMETER_APP_PREFERS_ICON = 0x00000200;
+
+ /**
* Application is allowed to use the legacy splash screen
* @hide
*/
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 95f1493..9f99dc9 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -301,7 +301,9 @@
granted to the system companion device manager service -->
<flag name="companion" value="0x800000" />
<!-- Additional flag from base permission type: this permission will be granted to the
- retail demo app, as defined by the OEM. -->
+ retail demo app, as defined by the OEM.
+ This flag has been replaced by the retail demo role and is a no-op since Android V.
+ -->
<flag name="retailDemo" value="0x1000000" />
<!-- Additional flag from base permission type: this permission will be granted to the
recents app. -->
diff --git a/libs/WindowManager/Shell/res/values-television/config.xml b/libs/WindowManager/Shell/res/values-television/config.xml
index 8d2e28b..464e132 100644
--- a/libs/WindowManager/Shell/res/values-television/config.xml
+++ b/libs/WindowManager/Shell/res/values-television/config.xml
@@ -57,4 +57,9 @@
- 0 for radial vanish + slide up
- 1 for fade out -->
<integer name="starting_window_exit_animation_type">1</integer>
+
+ <!-- Whether this device allows to use the appIcon as a fallback icon for the splash screen
+ window. If false, the splash screen will be a solid color splash screen whenever the
+ app has not provided a windowSplashScreenAnimatedIcon. -->
+ <bool name="config_canUseAppIconForSplashScreen">false</bool>
</resources>
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml
index 7436400..6a6f2b0 100644
--- a/libs/WindowManager/Shell/res/values/config.xml
+++ b/libs/WindowManager/Shell/res/values/config.xml
@@ -136,4 +136,9 @@
<!-- Whether DragAndDrop capability is enabled -->
<bool name="config_enableShellDragDrop">true</bool>
+
+ <!-- Whether this device allows to use the appIcon as a fallback icon for the splash screen
+ window. If false, the splash screen will be a solid color splash screen whenever the
+ app has not provided a windowSplashScreenAnimatedIcon. -->
+ <bool name="config_canUseAppIconForSplashScreen">true</bool>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
index 0c6adc9..43ae5f4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -23,6 +23,7 @@
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_APP_PREFERS_ICON;
import android.annotation.ColorInt;
import android.annotation.IntDef;
@@ -125,6 +126,7 @@
private final TransactionPool mTransactionPool;
private final SplashScreenWindowAttrs mTmpAttrs = new SplashScreenWindowAttrs();
private final Handler mSplashscreenWorkerHandler;
+ private final boolean mCanUseAppIconForSplashScreen;
@VisibleForTesting
final ColorCache mColorCache;
@@ -141,6 +143,8 @@
shellSplashscreenWorkerThread.start();
mSplashscreenWorkerHandler = shellSplashscreenWorkerThread.getThreadHandler();
mColorCache = new ColorCache(mContext, mSplashscreenWorkerHandler);
+ mCanUseAppIconForSplashScreen = context.getResources().getBoolean(
+ com.android.wm.shell.R.bool.config_canUseAppIconForSplashScreen);
}
/**
@@ -427,7 +431,10 @@
getWindowAttrs(context, mTmpAttrs);
mLastPackageContextConfigHash = context.getResources().getConfiguration().hashCode();
- final Drawable legacyDrawable = suggestType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
+ final @StartingWindowType int splashType =
+ suggestType == STARTING_WINDOW_TYPE_SPLASH_SCREEN && !canUseIcon(info)
+ ? STARTING_WINDOW_TYPE_SOLID_COLOR_SPLASH_SCREEN : suggestType;
+ final Drawable legacyDrawable = splashType == STARTING_WINDOW_TYPE_LEGACY_SPLASH_SCREEN
? peekLegacySplashscreenContent(context, mTmpAttrs) : null;
final ActivityInfo ai = info.targetActivityInfo != null
? info.targetActivityInfo
@@ -439,12 +446,17 @@
return new SplashViewBuilder(context, ai)
.setWindowBGColor(themeBGColor)
.overlayDrawable(legacyDrawable)
- .chooseStyle(suggestType)
+ .chooseStyle(splashType)
.setUiThreadInitConsumer(uiThreadInitConsumer)
.setAllowHandleSolidColor(info.allowHandleSolidColorSplashScreen())
.build();
}
+ private boolean canUseIcon(StartingWindowInfo info) {
+ return mCanUseAppIconForSplashScreen || mTmpAttrs.mSplashScreenIcon != null
+ || (info.startingWindowTypeParameter & TYPE_PARAMETER_APP_PREFERS_ICON) != 0;
+ }
+
private int getBGColorFromCache(ActivityInfo ai, IntSupplier windowBgColorSupplier) {
return mColorCache.getWindowColor(ai.packageName, mLastPackageContextConfigHash,
mTmpAttrs.mWindowBgColor, mTmpAttrs.mWindowBgResId, windowBgColorSupplier).mBgColor;
diff --git a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
index 1df1136..5b2ffec 100644
--- a/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/appcompat/AndroidTestTemplate.xml
@@ -95,14 +95,6 @@
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
index 1df1136..9f7d9fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/bubble/AndroidTestTemplate.xml
@@ -94,15 +94,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
index 1df1136..882b200 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/AndroidTestTemplate.xml
@@ -94,15 +94,9 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
+ value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
index ca182fa..6df6539 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/pip/csuiteDefaultTemplate.xml
@@ -57,6 +57,17 @@
<option name="test-file-name" value="WMShellFlickerTestsPipAppsCSuite.apk"/>
<option name="test-file-name" value="FlickerTestApp.apk"/>
</target_preparer>
+
+ <!-- Needed for installing apk's from Play Store -->
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="aapt-version" value="AAPT2"/>
+ <option name="throw-if-not-found" value="false"/>
+ <option name="install-arg" value="-d"/>
+ <option name="install-arg" value="-g"/>
+ <option name="install-arg" value="-r"/>
+ <option name="test-file-name" value="pstash://com.netflix.mediaclient"/>
+ </target_preparer>
+
<!-- Enable mocking GPS location by the test app -->
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<option name="run-command"
@@ -94,28 +105,8 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.pip.apps/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
-
- <!-- Needed for installing apk's from Play Store -->
- <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
- <option name="aapt-version" value="AAPT2"/>
- <option name="throw-if-not-found" value="false"/>
- <option name="install-arg" value="-d"/>
- <option name="install-arg" value="-g"/>
- <option name="install-arg" value="-r"/>
- <option name="test-file-name" value="pstash://com.netflix.mediaclient"/>
- </target_preparer>
</configuration>
diff --git a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
index 1df1136..51a55e35 100644
--- a/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/service/AndroidTestTemplate.xml
@@ -94,14 +94,6 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
index 1df1136..fdda597 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/AndroidTestTemplate.xml
@@ -94,15 +94,7 @@
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path"/>
<option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.bubbles/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.pip/files"/>
- <option name="directory-keys"
value="/data/user/0/com.android.wm.shell.flicker.splitscreen/files"/>
- <option name="directory-keys"
- value="/data/user/0/com.android.wm.shell.flicker.service/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
index a9082a6..012c4081 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/startingsurface/StartingWindowControllerTests.java
@@ -80,6 +80,7 @@
MockitoAnnotations.initMocks(this);
doReturn(mock(Display.class)).when(mDisplayManager).getDisplay(anyInt());
doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class));
+ doReturn(super.mContext.getResources()).when(mContext).getResources();
mShellInit = spy(new ShellInit(mMainExecutor));
mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mMainExecutor));
diff --git a/libs/input/PointerController.cpp b/libs/input/PointerController.cpp
index abd9284..576ebc1 100644
--- a/libs/input/PointerController.cpp
+++ b/libs/input/PointerController.cpp
@@ -24,6 +24,7 @@
#include <SkColor.h>
#include <android-base/stringprintf.h>
#include <android-base/thread_annotations.h>
+#include <com_android_input_flags.h>
#include <ftl/enum.h>
#include <mutex>
@@ -34,6 +35,8 @@
#define INDENT2 " "
#define INDENT3 " "
+namespace input_flags = com::android::input::flags;
+
namespace android {
namespace {
@@ -63,10 +66,20 @@
std::shared_ptr<PointerController> PointerController::create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController, bool enabled) {
+ SpriteController& spriteController, bool enabled, ControllerType type) {
// using 'new' to access non-public constructor
- std::shared_ptr<PointerController> controller = std::shared_ptr<PointerController>(
- new PointerController(policy, looper, spriteController, enabled));
+ std::shared_ptr<PointerController> controller;
+ switch (type) {
+ case ControllerType::MOUSE:
+ controller = std::shared_ptr<PointerController>(
+ new MousePointerController(policy, looper, spriteController, enabled));
+ break;
+ case ControllerType::LEGACY:
+ default:
+ controller = std::shared_ptr<PointerController>(
+ new PointerController(policy, looper, spriteController, enabled));
+ break;
+ }
/*
* Now we need to hook up the constructed PointerController object to its callbacks.
@@ -375,4 +388,13 @@
return dump;
}
+// --- MousePointerController ---
+
+MousePointerController::MousePointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper,
+ SpriteController& spriteController, bool enabled)
+ : PointerController(policy, looper, spriteController, enabled) {
+ PointerController::setPresentation(Presentation::POINTER);
+}
+
} // namespace android
diff --git a/libs/input/PointerController.h b/libs/input/PointerController.h
index aa7ca3c..08e19a0 100644
--- a/libs/input/PointerController.h
+++ b/libs/input/PointerController.h
@@ -47,7 +47,8 @@
public:
static std::shared_ptr<PointerController> create(
const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
- SpriteController& spriteController, bool enabled);
+ SpriteController& spriteController, bool enabled,
+ ControllerType type = ControllerType::LEGACY);
~PointerController() override;
@@ -75,7 +76,7 @@
void onDisplayInfosChangedLocked(const std::vector<gui::DisplayInfo>& displayInfos)
REQUIRES(getLock());
- std::string dump();
+ std::string dump() override;
protected:
using WindowListenerConsumer =
@@ -87,10 +88,10 @@
WindowListenerConsumer registerListener,
WindowListenerConsumer unregisterListener);
-private:
PointerController(const sp<PointerControllerPolicyInterface>& policy, const sp<Looper>& looper,
SpriteController& spriteController, bool enabled);
+private:
friend PointerControllerContext::LooperCallback;
friend PointerControllerContext::MessageHandler;
@@ -135,6 +136,24 @@
void clearSpotsLocked() REQUIRES(getLock());
};
+class MousePointerController : public PointerController {
+public:
+ /** A version of PointerController that controls one mouse pointer. */
+ MousePointerController(const sp<PointerControllerPolicyInterface>& policy,
+ const sp<Looper>& looper, SpriteController& spriteController,
+ bool enabled);
+
+ void setPresentation(Presentation) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void setSpots(const PointerCoords*, const uint32_t*, BitSet32, int32_t) override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+ void clearSpots() override {
+ LOG_ALWAYS_FATAL("Should not be called");
+ }
+};
+
} // namespace android
#endif // _UI_POINTER_CONTROLLER_H
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index 1c8ddcb..80b944f 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,11 +25,16 @@
alias(libs.plugins.kotlin.android) apply false
}
+val androidTop : String = File(rootDir, "../../../../..").canonicalPath
+
allprojects {
+ extra["androidTop"] = androidTop
extra["jetpackComposeVersion"] = "1.6.0-alpha08"
}
subprojects {
+ layout.buildDirectory.set(file("$androidTop/out/gradle-spa/$name"))
+
plugins.withType<AndroidBasePlugin> {
configure<BaseExtension> {
compileSdkVersion(34)
@@ -38,10 +43,11 @@
minSdk = 21
targetSdk = 34
}
+ }
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_17
- targetCompatibility = JavaVersion.VERSION_17
+ configure<JavaPluginExtension> {
+ toolchain {
+ languageVersion.set(JavaLanguageVersion.of(libs.versions.jvm.get()))
}
}
}
@@ -60,7 +66,7 @@
tasks.withType<KotlinCompile> {
kotlinOptions {
- jvmTarget = "17"
+ jvmTarget = libs.versions.jvm.get()
freeCompilerArgs = listOf("-Xjvm-default=all")
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/itemList/ItemOperatePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/itemList/ItemOperatePage.kt
index 98b27b7..6caec07 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/itemList/ItemOperatePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/itemList/ItemOperatePage.kt
@@ -17,9 +17,11 @@
package com.android.settingslib.spa.gallery.itemList
import android.os.Bundle
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.core.os.bundleOf
import androidx.navigation.NavType
import androidx.navigation.navArgument
@@ -79,13 +81,13 @@
entryList.add(
SettingsEntryBuilder.create("ItemOp", owner)
.setUiLayoutFn {
- val checked = rememberSaveable { mutableStateOf(false) }
+ var checked by rememberSaveable { mutableStateOf(false) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "Item operation: $opName"
- override val checked = checked
+ override val checked = { checked }
override val onCheckedChange =
- { newChecked: Boolean -> checked.value = newChecked }
+ { newChecked: Boolean -> checked = newChecked }
}
})
}.build(),
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
index 442ace8..0d85c0e3 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/MainSwitchPreferencePage.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,16 +18,17 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.Preference
@@ -43,13 +44,13 @@
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
- SettingsEntryBuilder.create( "MainSwitchPreference", owner)
+ SettingsEntryBuilder.create("MainSwitchPreference", owner)
.setUiLayoutFn {
SampleMainSwitchPreference()
}.build()
)
entryList.add(
- SettingsEntryBuilder.create( "MainSwitchPreference not changeable", owner)
+ SettingsEntryBuilder.create("MainSwitchPreference not changeable", owner)
.setUiLayoutFn {
SampleNotChangeableMainSwitchPreference()
}.build()
@@ -75,25 +76,25 @@
@Composable
private fun SampleMainSwitchPreference() {
- val checked = rememberSaveable { mutableStateOf(false) }
+ var checked by rememberSaveable { mutableStateOf(false) }
MainSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "MainSwitchPreference"
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleNotChangeableMainSwitchPreference() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
MainSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "Not changeable"
- override val changeable = stateOf(false)
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val changeable = { false }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt
index ce0ee18..f2225fa 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/SwitchPreferencePageProvider.kt
@@ -20,17 +20,18 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -98,32 +99,32 @@
@Composable
private fun SampleSwitchPreference() {
- val checked = rememberSaveable { mutableStateOf(false) }
+ var checked by rememberSaveable { mutableStateOf(false) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleSwitchPreferenceWithSummary() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { "With summary" }
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleSwitchPreferenceWithAsyncSummary() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
val summary = produceState(initialValue = " ") {
delay(1000L)
value = "Async summary"
@@ -132,34 +133,34 @@
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { summary.value }
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleNotChangeableSwitchPreference() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { "Not changeable" }
- override val changeable = stateOf(false)
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val changeable = { false }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
@Composable
private fun SampleSwitchPreferenceWithIcon() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive)
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt
index fc50745..19de31d 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/TwoTargetSwitchPreferencePageProvider.kt
@@ -18,17 +18,18 @@
import android.os.Bundle
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -89,32 +90,32 @@
@Composable
private fun SampleTwoTargetSwitchPreference() {
- val checked = rememberSaveable { mutableStateOf(false) }
+ var checked by rememberSaveable { mutableStateOf(false) }
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Composable
private fun SampleTwoTargetSwitchPreferenceWithSummary() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { "With summary" }
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Composable
private fun SampleTwoTargetSwitchPreferenceWithAsyncSummary() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
val summary = produceState(initialValue = " ") {
delay(1000L)
value = "Async summary"
@@ -123,22 +124,22 @@
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { summary.value }
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
@Composable
private fun SampleNotChangeableTwoTargetSwitchPreference() {
- val checked = rememberSaveable { mutableStateOf(true) }
+ var checked by rememberSaveable { mutableStateOf(true) }
TwoTargetSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { "Not changeable" }
- override val changeable = stateOf(false)
- override val checked = checked
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val changeable = { false }
+ override val checked = { checked }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
}) {}
}
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index aafae5f..fdb0471 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -18,6 +18,7 @@
agp = "8.1.2"
compose-compiler = "1.5.1"
dexmaker-mockito = "2.28.3"
+jvm = "17"
kotlin = "1.9.0"
truth = "1.1.5"
diff --git a/packages/SettingsLib/Spa/screenshot/build.gradle.kts b/packages/SettingsLib/Spa/screenshot/build.gradle.kts
new file mode 100644
index 0000000..03e9d7d
--- /dev/null
+++ b/packages/SettingsLib/Spa/screenshot/build.gradle.kts
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+plugins {
+ alias(libs.plugins.android.library)
+ alias(libs.plugins.kotlin.android)
+}
+
+val androidTop: String by extra
+
+android {
+ namespace = "com.android.settingslib.spa.screenshot"
+
+ defaultConfig {
+ testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ sourceSets {
+ sourceSets.getByName("main") {
+ java.setSrcDirs(
+ listOf("$androidTop/platform_testing/libraries/screenshot/src/main/java")
+ )
+ }
+ sourceSets.getByName("androidTest") {
+ kotlin.setSrcDirs(listOf("src"))
+ res.setSrcDirs(listOf("res"))
+ manifest.srcFile("AndroidManifest.xml")
+ }
+ }
+ buildFeatures {
+ compose = true
+ }
+}
+
+dependencies {
+ androidTestImplementation(project(":spa"))
+ androidTestImplementation(project(":testutils"))
+ androidTestImplementation(libs.dexmaker.mockito)
+}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
index c1d7188..e547d26 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/MainSwitchPreferenceScreenshotTest.kt
@@ -17,7 +17,6 @@
package com.android.settingslib.spa.screenshot.widget.preference
import androidx.compose.foundation.layout.Column
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.MainSwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -51,14 +50,14 @@
MainSwitchPreference(
object : SwitchPreferenceModel {
override val title = "MainSwitchPreference"
- override val checked = stateOf(false)
+ override val checked = { false }
override val onCheckedChange = null
})
MainSwitchPreference(object : SwitchPreferenceModel {
override val title = "Not changeable"
- override val changeable = stateOf(false)
- override val checked = stateOf(true)
+ override val changeable = { false }
+ override val checked = { true }
override val onCheckedChange = null
})
}
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
index a688e11..6966a74 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/SwitchPreferenceScreenshotTest.kt
@@ -20,7 +20,6 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.screenshot.util.SettingsScreenshotTestRule
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -65,7 +64,7 @@
private fun SampleSwitchPreference() {
SwitchPreference(object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val checked = stateOf(false)
+ override val checked = { false }
override val onCheckedChange = null
})
}
@@ -75,7 +74,7 @@
SwitchPreference(object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { "With summary" }
- override val checked = stateOf(true)
+ override val checked = { true }
override val onCheckedChange = null
})
}
@@ -85,8 +84,8 @@
SwitchPreference(object : SwitchPreferenceModel {
override val title = "SwitchPreference"
override val summary = { "Not changeable" }
- override val changeable = stateOf(false)
- override val checked = stateOf(true)
+ override val changeable = { false }
+ override val checked = { true }
override val onCheckedChange = null
})
}
@@ -95,7 +94,7 @@
private fun SampleSwitchPreferenceWithIcon() {
SwitchPreference(object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val checked = stateOf(true)
+ override val checked = { true }
override val onCheckedChange = null
override val icon = @Composable {
SettingsIcon(imageVector = Icons.Outlined.AirplanemodeActive)
diff --git a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
index 8f0abc0..72c5cb8 100644
--- a/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
+++ b/packages/SettingsLib/Spa/screenshot/src/com/android/settingslib/spa/screenshot/widget/preference/TwoTargetSwitchPreferenceScreenshotTest.kt
@@ -50,21 +50,21 @@
Column {
TwoTargetSwitchPreference(object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val checked = stateOf(false)
+ override val checked = { false }
override val onCheckedChange = null
}) {}
TwoTargetSwitchPreference(object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
override val summary = { "With summary" }
- override val checked = stateOf(true)
+ override val checked = { true }
override val onCheckedChange = null
}) {}
TwoTargetSwitchPreference(object : SwitchPreferenceModel {
override val title = "Not changeable"
- override val changeable = stateOf(false)
- override val checked = stateOf(true)
+ override val changeable = { false }
+ override val checked = { true }
override val onCheckedChange = null
}) {}
}
diff --git a/packages/SettingsLib/Spa/settings.gradle.kts b/packages/SettingsLib/Spa/settings.gradle.kts
index b8dfae3..e003c81 100644
--- a/packages/SettingsLib/Spa/settings.gradle.kts
+++ b/packages/SettingsLib/Spa/settings.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -49,3 +49,6 @@
include(":testutils")
include(":SettingsLibColor")
project(":SettingsLibColor").projectDir = File(rootDir, "../Color")
+
+// Uncomment this for screenshot
+// include(":screenshot")
\ No newline at end of file
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 189c2dd..4335173 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -63,7 +63,7 @@
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.7.4")
+ api("androidx.navigation:navigation-compose:2.7.5")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
index 8bfcff8..83cb549 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
@@ -16,10 +16,16 @@
package com.android.settingslib.spa.framework.util
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.lifecycleScope
+import androidx.lifecycle.repeatOnLifecycle
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
+import kotlinx.coroutines.launch
/**
* Returns a [Flow] whose values are a list which containing the results of applying the given
@@ -46,3 +52,19 @@
*/
fun <T1, T2> Flow<T1>.waitFirst(otherFlow: Flow<T2>): Flow<T1> =
combine(otherFlow.take(1)) { value, _ -> value }
+
+
+/**
+ * Collects the latest value of given flow with a provided action with [LifecycleOwner].
+ */
+fun <T> Flow<T>.collectLatestWithLifecycle(
+ lifecycleOwner: LifecycleOwner,
+ minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
+ action: suspend (value: T) -> Unit,
+) {
+ lifecycleOwner.lifecycleScope.launch {
+ lifecycleOwner.repeatOnLifecycle(minActiveState) {
+ collectLatest(action)
+ }
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/livedata/LiveDataExt.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/livedata/LiveDataExt.kt
new file mode 100644
index 0000000..3d330fe
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/livedata/LiveDataExt.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spa.livedata
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.lifecycle.LiveData
+
+/**
+ * Starts observing this LiveData and represents its values via State and Callback.
+ *
+ * Every time there would be new value posted into the LiveData the returned State will be updated
+ * causing recomposition of every Callback usage.
+ */
+@Composable
+fun <T> LiveData<T>.observeAsCallback(): () -> T? {
+ val isAllowed by observeAsState()
+ return { isAllowed }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
index 35c34d4..fc8de80 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/MainSwitchPreference.kt
@@ -24,7 +24,6 @@
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsShape
import com.android.settingslib.spa.framework.theme.SettingsTheme
@@ -35,7 +34,7 @@
EntryHighlight {
Surface(
modifier = Modifier.padding(SettingsDimension.itemPaddingEnd),
- color = when (model.checked.value) {
+ color = when (model.checked()) {
true -> MaterialTheme.colorScheme.primaryContainer
else -> MaterialTheme.colorScheme.secondaryContainer
},
@@ -43,8 +42,8 @@
) {
InternalSwitchPreference(
title = model.title,
- checked = model.checked.value,
- changeable = model.changeable.value,
+ checked = model.checked(),
+ changeable = model.changeable(),
onCheckedChange = model.onCheckedChange,
paddingStart = 20.dp,
paddingEnd = 20.dp,
@@ -61,12 +60,12 @@
Column {
MainSwitchPreference(object : SwitchPreferenceModel {
override val title = "Use Dark theme"
- override val checked = true.toState()
+ override val checked = { true }
override val onCheckedChange: (Boolean) -> Unit = {}
})
MainSwitchPreference(object : SwitchPreferenceModel {
override val title = "Use Dark theme"
- override val checked = false.toState()
+ override val checked = { false }
override val onCheckedChange: (Boolean) -> Unit = {}
})
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
index 12afe92..aceb545 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/SwitchPreference.kt
@@ -25,13 +25,11 @@
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.AirplanemodeActive
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.EntryHighlight
@@ -67,15 +65,15 @@
*
* This can be `null` during the data loading before the data is available.
*/
- val checked: State<Boolean?>
+ val checked: () -> Boolean?
/**
* Indicates whether this [SwitchPreference] is changeable.
*
* Not changeable [SwitchPreference] will be displayed in disabled style.
*/
- val changeable: State<Boolean>
- get() = stateOf(true)
+ val changeable: () -> Boolean
+ get() = { true }
/**
* The switch change handler of this [SwitchPreference].
@@ -97,8 +95,8 @@
title = model.title,
summary = model.summary,
icon = model.icon,
- checked = model.checked.value,
- changeable = model.changeable.value,
+ checked = model.checked(),
+ changeable = model.changeable(),
onCheckedChange = model.onCheckedChange,
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
index 9a6580c..7eed745 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreference.kt
@@ -34,8 +34,8 @@
icon = icon,
) {
SettingsSwitch(
- checked = model.checked.value,
- changeable = { model.changeable.value },
+ checked = model.checked(),
+ changeable = model.changeable,
onCheckedChange = model.onCheckedChange,
)
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/FlowsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/FlowsTest.kt
index 71d69b2..a64b43b 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/FlowsTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/framework/util/FlowsTest.kt
@@ -16,7 +16,9 @@
package com.android.settingslib.spa.framework.util
+import androidx.lifecycle.testing.TestLifecycleOwner
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.waitUntil
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.count
import kotlinx.coroutines.flow.emptyFlow
@@ -85,4 +87,16 @@
assertThat(outputFlow.toList()).containsExactly("A")
}
+
+ @Test
+ fun collectLatestWithLifecycle() {
+ val mainFlow = flowOf("A")
+ var actual: String? = null
+
+ mainFlow.collectLatestWithLifecycle(TestLifecycleOwner()) {
+ actual = it
+ }
+
+ waitUntil { actual == "A" }
+ }
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt
index 6588cf5..8ce6979 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/MainSwitchPreferenceTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
package com.android.settingslib.spa.widget.preference
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
@@ -28,7 +30,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -81,13 +82,13 @@
@Composable
private fun TestMainSwitchPreference(changeable: Boolean) {
- val checked = rememberSaveable { mutableStateOf(false) }
+ var checked by rememberSaveable { mutableStateOf(false) }
MainSwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = TITLE
- override val checked = checked
- override val changeable = stateOf(changeable)
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val changeable = { changeable }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt
index d6c8fbc..0b2127d 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/SwitchPreferenceTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
package com.android.settingslib.spa.widget.preference
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
@@ -28,7 +30,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,13 +80,13 @@
@Composable
private fun TestSwitchPreference(changeable: Boolean) {
- val checked = rememberSaveable { mutableStateOf(false) }
+ var checked by rememberSaveable { mutableStateOf(false) }
SwitchPreference(remember {
object : SwitchPreferenceModel {
override val title = "SwitchPreference"
- override val checked = checked
- override val changeable = stateOf(changeable)
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val changeable = { changeable }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
})
}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreferenceTest.kt
index c49e92d..3455851 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/TwoTargetSwitchPreferenceTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
package com.android.settingslib.spa.widget.preference
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsOff
import androidx.compose.ui.test.assertIsOn
@@ -28,7 +30,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
@@ -41,9 +42,8 @@
@Test
fun title_displayed() {
- val checked = mutableStateOf(false)
composeTestRule.setContent {
- TestTwoTargetSwitchPreference(checked = checked, changeable = true)
+ TestTwoTargetSwitchPreference(changeable = true)
}
composeTestRule.onNodeWithText("TwoTargetSwitchPreference").assertIsDisplayed()
@@ -51,9 +51,8 @@
@Test
fun toggleable_initialStateIsCorrect() {
- val checked = mutableStateOf(false)
composeTestRule.setContent {
- TestTwoTargetSwitchPreference(checked = checked, changeable = true)
+ TestTwoTargetSwitchPreference(changeable = true)
}
composeTestRule.onNode(isToggleable()).assertIsOff()
@@ -61,9 +60,8 @@
@Test
fun toggleable_changeable_withEffect() {
- val checked = mutableStateOf(false)
composeTestRule.setContent {
- TestTwoTargetSwitchPreference(checked = checked, changeable = true)
+ TestTwoTargetSwitchPreference(changeable = true)
}
composeTestRule.onNode(isToggleable()).performClick()
@@ -72,9 +70,8 @@
@Test
fun toggleable_notChangeable_noEffect() {
- val checked = mutableStateOf(false)
composeTestRule.setContent {
- TestTwoTargetSwitchPreference(checked = checked, changeable = false)
+ TestTwoTargetSwitchPreference(changeable = false)
}
composeTestRule.onNode(isToggleable()).performClick()
@@ -83,10 +80,9 @@
@Test
fun clickable_canBeClick() {
- val checked = mutableStateOf(false)
var clicked = false
composeTestRule.setContent {
- TestTwoTargetSwitchPreference(checked = checked, changeable = false) {
+ TestTwoTargetSwitchPreference(changeable = false) {
clicked = true
}
}
@@ -98,17 +94,17 @@
@Composable
private fun TestTwoTargetSwitchPreference(
- checked: MutableState<Boolean>,
changeable: Boolean,
onClick: () -> Unit = {},
) {
+ var checked by rememberSaveable { mutableStateOf(false) }
TwoTargetSwitchPreference(
model = remember {
object : SwitchPreferenceModel {
override val title = "TwoTargetSwitchPreference"
- override val checked = checked
- override val changeable = stateOf(changeable)
- override val onCheckedChange = { newChecked: Boolean -> checked.value = newChecked }
+ override val checked = { checked }
+ override val changeable = { changeable }
+ override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
}
},
onClick = onClick,
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt
index 7a11499..99c6a3f 100644
--- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt
+++ b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/FlowTestUtil.kt
@@ -18,9 +18,17 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.withTimeoutOrNull
suspend fun <T> Flow<T>.firstWithTimeoutOrNull(timeMillis: Long = 500): T? =
withTimeoutOrNull(timeMillis) {
first()
}
+
+suspend fun <T> Flow<T>.toListWithTimeout(timeMillis: Long = 500): List<T> {
+ val list = mutableListOf<T>()
+ return withTimeoutOrNull(timeMillis) {
+ toList(list)
+ } ?: list
+}
diff --git a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
index 17c139b..d992114 100644
--- a/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
+++ b/packages/SettingsLib/SpaPrivileged/AndroidManifest.xml
@@ -18,5 +18,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib.spaprivileged">
<uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
</manifest>
-
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt
new file mode 100644
index 0000000..8e702ea
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepository.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings
+import kotlin.properties.ReadWriteProperty
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.flow.Flow
+
+fun Context.settingsGlobalBoolean(name: String): ReadWriteProperty<Any?, Boolean> =
+ SettingsGlobalBooleanDelegate(this, name)
+
+fun Context.settingsGlobalBooleanFlow(name: String): Flow<Boolean> {
+ val value by settingsGlobalBoolean(name)
+ return settingsGlobalFlow(name) { value }
+}
+
+private class SettingsGlobalBooleanDelegate(context: Context, private val name: String) :
+ ReadWriteProperty<Any?, Boolean> {
+
+ private val contentResolver: ContentResolver = context.contentResolver
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): Boolean =
+ Settings.Global.getInt(contentResolver, name, 0) != 0
+
+ override fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) {
+ Settings.Global.putInt(contentResolver, name, if (value) 1 else 0)
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalChangeRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalChangeRepository.kt
new file mode 100644
index 0000000..4098987
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalChangeRepository.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.Context
+import kotlinx.coroutines.flow.Flow
+
+fun Context.settingsGlobalChangeFlow(name: String, sendInitialValue: Boolean = true): Flow<Unit> =
+ settingsGlobalFlow(name, sendInitialValue) { }
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalFlow.kt
new file mode 100644
index 0000000..de5c558
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalFlow.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.Context
+import android.database.ContentObserver
+import android.os.Handler
+import android.provider.Settings
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.flowOn
+
+internal fun <T> Context.settingsGlobalFlow(
+ name: String,
+ sendInitialValue: Boolean = true,
+ value: () -> T,
+): Flow<T> = callbackFlow {
+ val contentObserver = object : ContentObserver(Handler.getMain()) {
+ override fun onChange(selfChange: Boolean) {
+ trySend(value())
+ }
+ }
+ val uri = Settings.Global.getUriFor(name)
+ contentResolver.registerContentObserver(uri, false, contentObserver)
+ if (sendInitialValue) {
+ trySend(value())
+ }
+
+ awaitClose { contentResolver.unregisterContentObserver(contentObserver) }
+}.conflate().flowOn(Dispatchers.Default)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
index 155905b..36877af 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItem.kt
@@ -17,7 +17,6 @@
package com.android.settingslib.spaprivileged.template.app
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
@@ -25,8 +24,8 @@
@Composable
fun <T : AppRecord> AppListItemModel<T>.AppListSwitchItem(
- checked: State<Boolean?>,
- changeable: State<Boolean>,
+ checked: () -> Boolean?,
+ changeable: () -> Boolean,
onCheckedChange: ((newChecked: Boolean) -> Unit)?,
) {
SwitchPreference(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
index 99d3840..bea14c3 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItem.kt
@@ -17,7 +17,6 @@
package com.android.settingslib.spaprivileged.template.app
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.preference.TwoTargetSwitchPreference
@@ -26,8 +25,8 @@
@Composable
fun <T : AppRecord> AppListItemModel<T>.AppListTwoTargetSwitchItem(
onClick: () -> Unit,
- checked: State<Boolean?>,
- changeable: State<Boolean>,
+ checked: () -> Boolean?,
+ changeable: () -> Boolean,
onCheckedChange: ((newChecked: Boolean) -> Unit)?,
) {
TwoTargetSwitchPreference(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
index e9da88e..7bd7fbe 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppList.kt
@@ -22,11 +22,7 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
-import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.runtime.remember
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.asyncMapItem
import com.android.settingslib.spa.framework.util.filterItem
import com.android.settingslib.spaprivileged.model.app.AppOpsController
@@ -97,8 +93,9 @@
with(packageManagers) {
AppOpPermissionRecord(
app = app,
- hasRequestBroaderPermission =
- broaderPermission?.let { app.hasRequestPermission(it) } ?: false,
+ hasRequestBroaderPermission = broaderPermission?.let {
+ app.hasRequestPermission(it)
+ } ?: false,
hasRequestPermission = hasRequestPermission,
appOpsController = createAppOpsController(app),
)
@@ -138,22 +135,19 @@
* (This means pre-M gets approval during install time; M apps gets approval during runtime).
*/
@Composable
- override fun isAllowed(record: AppOpPermissionRecord): State<Boolean?> {
+ override fun isAllowed(record: AppOpPermissionRecord): () -> Boolean? {
if (record.hasRequestBroaderPermission) {
// Broader permission trumps the specific permission.
- return stateOf(true)
+ return { true }
}
val mode = record.appOpsController.mode.observeAsState()
- return remember {
- derivedStateOf {
- when (mode.value) {
- null -> null
- MODE_ALLOWED -> true
- MODE_DEFAULT ->
- with(packageManagers) { record.app.hasGrantPermission(permission) }
- else -> false
- }
+ return {
+ when (mode.value) {
+ null -> null
+ MODE_ALLOWED -> true
+ MODE_DEFAULT -> with(packageManagers) { record.app.hasGrantPermission(permission) }
+ else -> false
}
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index 62c5f70..3380b7d 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -22,9 +22,10 @@
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.State
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.os.bundleOf
@@ -165,14 +166,16 @@
context: Context,
private val listModel: TogglePermissionAppListModel<T>,
private val record: T,
- isAllowed: State<Boolean?>,
+ isAllowed: () -> Boolean?,
) : SwitchPreferenceModel {
+ private var appChangeable by mutableStateOf(true)
+
override val title: String = context.getString(listModel.switchTitleResId)
override val checked = isAllowed
- override val changeable = mutableStateOf(true)
+ override val changeable = { appChangeable }
fun initState() {
- changeable.value = listModel.isChangeable(record)
+ appChangeable = listModel.isChangeable(record)
}
override val onCheckedChange: (Boolean) -> Unit = { newChecked ->
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 1ab6230..8704f20 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.State
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
@@ -64,7 +63,7 @@
* Gets whether the permission is allowed for the given app.
*/
@Composable
- fun isAllowed(record: T): State<Boolean?>
+ fun isAllowed(record: T): () -> Boolean?
/**
* Gets whether the permission on / off is changeable for the given app.
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 3ab27367..785f779 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -156,12 +156,12 @@
)
}
val restrictedMode by restrictionsProviderFactory.rememberRestrictedMode(restrictions)
- val allowed by listModel.isAllowed(record)
+ val allowed = listModel.isAllowed(record)
return RestrictedSwitchPreference.getSummary(
context = context,
restrictedModeSupplier = { restrictedMode },
- summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed) },
- checked = { allowed },
+ summaryIfNoRestricted = { getSummaryIfNoRestricted(allowed()) },
+ checked = allowed,
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
index d17e0c7..e41976f 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -28,7 +28,6 @@
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.toggleableState
import androidx.compose.ui.state.ToggleableState
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.widget.preference.SwitchPreference
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.framework.compose.getPlaceholder
@@ -97,21 +96,21 @@
context = context,
restrictedModeSupplier = { restrictedMode },
summaryIfNoRestricted = model.summary,
- checked = { model.checked.value },
+ checked = model.checked,
)
override val checked = when (restrictedMode) {
- null -> stateOf(null)
+ null -> ({ null })
is NoRestricted -> model.checked
- is BaseUserRestricted -> stateOf(false)
+ is BaseUserRestricted -> ({ false })
is BlockedByAdmin -> model.checked
}
override val changeable = when (restrictedMode) {
- null -> stateOf(false)
+ null -> ({ false })
is NoRestricted -> model.changeable
- is BaseUserRestricted -> stateOf(false)
- is BlockedByAdmin -> stateOf(false)
+ is BaseUserRestricted -> ({ false })
+ is BlockedByAdmin -> ({ false })
}
override val onCheckedChange = when (restrictedMode) {
@@ -137,7 +136,7 @@
onClick = { restrictedMode.sendShowAdminSupportDetailsIntent() },
)
.semantics {
- this.toggleableState = ToggleableState(checked.value)
+ this.toggleableState = ToggleableState(checked())
},
) { content() }
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepositoryTest.kt
new file mode 100644
index 0000000..996c2d5
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalBooleanRepositoryTest.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.Context
+import android.provider.Settings
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsGlobalBooleanRepositoryTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun getValue_setTrue_returnTrue() {
+ Settings.Global.putInt(context.contentResolver, TEST_NAME, 1)
+
+ val value by context.settingsGlobalBoolean(TEST_NAME)
+
+ assertThat(value).isTrue()
+ }
+
+ @Test
+ fun getValue_setFalse_returnFalse() {
+ Settings.Global.putInt(context.contentResolver, TEST_NAME, 0)
+
+ val value by context.settingsGlobalBoolean(TEST_NAME)
+
+ assertThat(value).isFalse()
+ }
+
+ @Test
+ fun setValue_setTrue_returnTrue() {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+
+ value = true
+
+ assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 0)).isEqualTo(1)
+ }
+
+ @Test
+ fun setValue_setFalse_returnFalse() {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+
+ value = false
+
+ assertThat(Settings.Global.getInt(context.contentResolver, TEST_NAME, 1)).isEqualTo(0)
+ }
+
+ @Test
+ fun settingsGlobalBooleanFlow_valueNotChanged() = runBlocking {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+
+ val flow = context.settingsGlobalBooleanFlow(TEST_NAME)
+
+ assertThat(flow.firstWithTimeoutOrNull()).isFalse()
+ }
+
+ @Test
+ fun settingsGlobalBooleanFlow_collectAfterValueChanged_onlyKeepLatest() = runBlocking {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+
+ val flow = context.settingsGlobalBooleanFlow(TEST_NAME)
+ value = true
+
+ assertThat(flow.firstWithTimeoutOrNull()).isTrue()
+ }
+
+ @Test
+ fun settingsGlobalBooleanFlow_collectBeforeValueChanged_getBoth() = runBlocking {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+
+ val listDeferred = async {
+ context.settingsGlobalBooleanFlow(TEST_NAME).toListWithTimeout()
+ }
+ delay(100)
+ value = true
+
+ assertThat(listDeferred.await()).containsExactly(false, true).inOrder()
+ }
+
+ private companion object {
+ const val TEST_NAME = "test_boolean_delegate"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalChangeRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalChangeRepositoryTest.kt
new file mode 100644
index 0000000..37e00e6
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/settingsprovider/SettingsGlobalChangeRepositoryTest.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.spaprivileged.settingsprovider
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull
+import com.android.settingslib.spa.testutils.toListWithTimeout
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.async
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class SettingsGlobalChangeRepositoryTest {
+
+ private val context: Context = ApplicationProvider.getApplicationContext()
+
+ @Test
+ fun settingsGlobalChangeFlow_sendInitialValueTrue() = runBlocking {
+ val flow = context.settingsGlobalChangeFlow(name = TEST_NAME, sendInitialValue = true)
+
+ assertThat(flow.firstWithTimeoutOrNull()).isNotNull()
+ }
+
+ @Test
+ fun settingsGlobalChangeFlow_sendInitialValueFalse() = runBlocking {
+ val flow = context.settingsGlobalChangeFlow(name = TEST_NAME, sendInitialValue = false)
+
+ assertThat(flow.firstWithTimeoutOrNull()).isNull()
+ }
+
+ @Test
+ fun settingsGlobalChangeFlow_collectAfterValueChanged_onlyKeepLatest() = runBlocking {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+
+ val flow = context.settingsGlobalBooleanFlow(TEST_NAME)
+ value = true
+
+ assertThat(flow.toListWithTimeout()).hasSize(1)
+ }
+
+ @Test
+ fun settingsGlobalChangeFlow_collectBeforeValueChanged_getBoth() = runBlocking {
+ var value by context.settingsGlobalBoolean(TEST_NAME)
+ value = false
+
+ val listDeferred = async {
+ context.settingsGlobalChangeFlow(TEST_NAME).toListWithTimeout()
+ }
+ delay(100)
+ value = true
+
+ assertThat(listDeferred.await()).hasSize(2)
+ }
+
+ private companion object {
+ const val TEST_NAME = "test_boolean_delegate"
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
index c29d7c2..73dd295 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListSwitchItemTest.kt
@@ -27,7 +27,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -43,8 +42,8 @@
fun appLabel_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(null),
- changeable = stateOf(false),
+ checked = { null },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -56,8 +55,8 @@
fun summary_displayed() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(null),
- changeable = stateOf(false),
+ checked = { null },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -69,8 +68,8 @@
fun switch_checkIsNull() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(null),
- changeable = stateOf(false),
+ checked = { null },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -82,8 +81,8 @@
fun switch_checked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(true),
- changeable = stateOf(false),
+ checked = { true },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -95,8 +94,8 @@
fun switch_notChecked() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(false),
- changeable = stateOf(false),
+ checked = { false },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -108,8 +107,8 @@
fun switch_changeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(false),
- changeable = stateOf(true),
+ checked = { false },
+ changeable = { true },
onCheckedChange = {},
)
}
@@ -121,8 +120,8 @@
fun switch_notChangeable() {
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(false),
- changeable = stateOf(false),
+ checked = { false },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -135,8 +134,8 @@
var switchClicked = false
composeTestRule.setContent {
ITEM_MODEL.AppListSwitchItem(
- checked = stateOf(false),
- changeable = stateOf(true),
+ checked = { false },
+ changeable = { true },
onCheckedChange = { switchClicked = true },
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
index 644a2d7..d3cfb2d 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppListTwoTargetSwitchItemTest.kt
@@ -27,7 +27,6 @@
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
@@ -44,8 +43,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(null),
- changeable = stateOf(false),
+ checked = { null },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -58,8 +57,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(null),
- changeable = stateOf(false),
+ checked = { null },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -73,8 +72,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = { titleClicked = true },
- checked = stateOf(false),
- changeable = stateOf(false),
+ checked = { false },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -89,8 +88,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(null),
- changeable = stateOf(false),
+ checked = { null },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -103,8 +102,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(true),
- changeable = stateOf(false),
+ checked = { true },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -117,8 +116,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(false),
- changeable = stateOf(false),
+ checked = { false },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -131,8 +130,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(false),
- changeable = stateOf(true),
+ checked = { false },
+ changeable = { true },
onCheckedChange = {},
)
}
@@ -145,8 +144,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(false),
- changeable = stateOf(false),
+ checked = { false },
+ changeable = { false },
onCheckedChange = {},
)
}
@@ -160,8 +159,8 @@
composeTestRule.setContent {
ITEM_MODEL.AppListTwoTargetSwitchItem(
onClick = {},
- checked = stateOf(false),
- changeable = stateOf(true),
+ checked = { false },
+ changeable = { true },
onCheckedChange = { switchClicked = true },
)
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
index eb2055f..3b2fe0f 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/AppOpPermissionAppListTest.kt
@@ -20,7 +20,6 @@
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
-import androidx.compose.runtime.State
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.lifecycle.MutableLiveData
import androidx.test.core.app.ApplicationProvider
@@ -307,9 +306,9 @@
}
private fun getIsAllowed(record: AppOpPermissionRecord): Boolean? {
- lateinit var isAllowedState: State<Boolean?>
+ lateinit var isAllowedState: () -> Boolean?
composeTestRule.setContent { isAllowedState = listModel.isAllowed(record) }
- return isAllowedState.value
+ return isAllowedState()
}
private inner class TestAppOpPermissionAppListModel :
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
index a13c483..00ba9b4 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreferenceTest.kt
@@ -49,8 +49,9 @@
private val switchPreferenceModel = object : SwitchPreferenceModel {
override val title = TITLE
- override val checked = mutableStateOf(true)
- override val onCheckedChange: (Boolean) -> Unit = { checked.value = it }
+ private val checkedState = mutableStateOf(true)
+ override val checked = { checkedState.value }
+ override val onCheckedChange: (Boolean) -> Unit = { checkedState.value = it }
}
@Test
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
index 1bfc7ed..1790313 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/tests/testutils/TestTogglePermissionAppListModel.kt
@@ -18,7 +18,9 @@
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
import com.android.settingslib.spaprivileged.template.app.TogglePermissionAppListModel
import com.android.settingslib.spaprivileged.test.R
import kotlinx.coroutines.flow.Flow
@@ -31,7 +33,7 @@
override val switchTitleResId = R.string.test_permission_switch_title
override val footerResId = R.string.test_permission_footer
- private val isAllowedState = mutableStateOf(isAllowed)
+ private var isAllowed by mutableStateOf(isAllowed)
override fun transformItem(app: ApplicationInfo) = TestAppRecord(app = app)
@@ -39,11 +41,11 @@
recordListFlow
@Composable
- override fun isAllowed(record: TestAppRecord) = isAllowedState
+ override fun isAllowed(record: TestAppRecord) = { isAllowed }
override fun isChangeable(record: TestAppRecord) = isChangeable
override fun setAllowed(record: TestAppRecord, newAllowed: Boolean) {
- isAllowedState.value = newAllowed
+ isAllowed = newAllowed
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 24083b6..e9f4b5c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -1404,7 +1404,7 @@
lowBatteryColorRes);
}
if (rightBattery >= 0) {
- if (!spannableBuilder.isEmpty()) {
+ if (spannableBuilder.length() > 0) {
spannableBuilder.append(" ");
}
String right = res.getString(
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 4c9436b..89a8dd9 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -327,4 +327,12 @@
<!-- Whether wifi is always requested by default. -->
<bool name="def_enable_wifi_always_requested">false</bool>
+
+ <!-- Default for Settings.BATTERY_CHARGING_STATE_UPDATE_DELAY in millisecond.
+ -1 means system internal default value is used. -->
+ <integer name="def_battery_charging_state_update_delay_ms">-1</integer>
+
+ <!-- Default for Settings.BATTERY_CHARGING_STATE_ENFORCE_LEVEL.
+ -1 means system internal default value is used. -->
+ <integer name="def_battery_charging_state_enforce_level">-1</integer>
</resources>
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 785003a..b0abf92 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -3792,7 +3792,7 @@
}
private final class UpgradeController {
- private static final int SETTINGS_VERSION = 223;
+ private static final int SETTINGS_VERSION = 224;
private final int mUserId;
@@ -5965,8 +5965,23 @@
// Do nothing. Leave the value as is.
}
}
+ }
- currentVersion = 223;
+ currentVersion = 223;
+
+ // Version 223: make charging constraint update criteria customizable.
+ if (currentVersion == 223) {
+ initGlobalSettingsDefaultValLocked(
+ Global.BATTERY_CHARGING_STATE_UPDATE_DELAY,
+ getContext().getResources().getInteger(
+ R.integer.def_battery_charging_state_update_delay_ms));
+
+ initGlobalSettingsDefaultValLocked(
+ Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL,
+ getContext().getResources().getInteger(
+ R.integer.def_battery_charging_state_enforce_level)
+ );
+ currentVersion = 224;
}
// vXXX: Add new settings above this point.
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 1f171ba..2e174e2 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -145,6 +145,7 @@
Settings.Global.AUTOFILL_MAX_VISIBLE_DATASETS,
Settings.Global.AUTO_TIME_ZONE_EXPLICIT,
Settings.Global.AVERAGE_TIME_TO_DISCHARGE,
+ Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL,
Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY,
Settings.Global.BATTERY_ESTIMATES_LAST_UPDATE_TIME,
Settings.Global.BROADCAST_BG_CONSTANTS,
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index c274911..069ba6c 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -53,6 +53,14 @@
}
flag {
+ name: "keyguard_bottom_area_refactor"
+ namespace: "systemui"
+ description: "Bottom area of keyguard refactor move into KeyguardRootView. Includes "
+ "lock icon and others."
+ bug: "290652751"
+}
+
+flag {
name: "visual_interruptions_refactor"
namespace: "systemui"
description: "Enables the refactored version of the code to decide when notifications "
@@ -66,3 +74,11 @@
description: "Adds haptic feedback to the brightness slider."
bug: "296467915"
}
+
+flag {
+ name: "keyguard_shade_migration_nssl"
+ namespace: "systemui"
+ description: "Moves NSSL into a shared element between the notification_panel and "
+ "keyguard_root_view."
+ bug: "278054201"
+}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
index f2b7b32..56970d7 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerScene.kt
@@ -19,6 +19,7 @@
import android.app.AlertDialog
import android.app.Dialog
import android.content.DialogInterface
+import android.content.res.Configuration
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.snap
@@ -50,6 +51,7 @@
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -63,6 +65,7 @@
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
@@ -136,7 +139,7 @@
modifier: Modifier = Modifier,
) {
val backgroundColor = MaterialTheme.colorScheme.surface
- val windowSizeClass = LocalWindowSizeClass.current
+ val layout = calculateLayout()
Box(modifier) {
Canvas(Modifier.element(Bouncer.Elements.Background).fillMaxSize()) {
@@ -146,22 +149,30 @@
val childModifier = Modifier.element(Bouncer.Elements.Content).fillMaxSize()
val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
- when {
- windowSizeClass.widthSizeClass == WindowWidthSizeClass.Expanded ->
+ when (layout) {
+ Layout.STANDARD ->
+ Bouncer(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ isUserInputAreaVisible = true,
+ modifier = childModifier,
+ )
+ Layout.SIDE_BY_SIDE ->
SideBySide(
viewModel = viewModel,
dialogFactory = dialogFactory,
+ isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
modifier = childModifier,
)
- isFullScreenUserSwitcherEnabled &&
- windowSizeClass.widthSizeClass == WindowWidthSizeClass.Medium ->
+ Layout.STACKED ->
Stacked(
viewModel = viewModel,
dialogFactory = dialogFactory,
+ isUserSwitcherVisible = isFullScreenUserSwitcherEnabled,
modifier = childModifier,
)
- else ->
- Bouncer(
+ Layout.SPLIT ->
+ Split(
viewModel = viewModel,
dialogFactory = dialogFactory,
modifier = childModifier,
@@ -178,11 +189,10 @@
private fun Bouncer(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
+ isUserInputAreaVisible: Boolean,
modifier: Modifier = Modifier,
) {
val message: BouncerViewModel.MessageViewModel by viewModel.message.collectAsState()
- val authMethodViewModel: AuthMethodBouncerViewModel? by
- viewModel.authMethodViewModel.collectAsState()
val dialogMessage: String? by viewModel.throttlingDialogMessage.collectAsState()
var dialog: Dialog? by remember { mutableStateOf(null) }
@@ -204,25 +214,11 @@
}
Box(Modifier.weight(1f)) {
- when (val nonNullViewModel = authMethodViewModel) {
- is PinBouncerViewModel ->
- PinBouncer(
- viewModel = nonNullViewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- is PasswordBouncerViewModel ->
- PasswordBouncer(
- viewModel = nonNullViewModel,
- modifier = Modifier.align(Alignment.Center),
- )
- is PatternBouncerViewModel ->
- PatternBouncer(
- viewModel = nonNullViewModel,
- modifier =
- Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
- .align(Alignment.BottomCenter),
- )
- else -> Unit
+ if (isUserInputAreaVisible) {
+ UserInputArea(
+ viewModel = viewModel,
+ modifier = Modifier.align(Alignment.Center),
+ )
}
}
@@ -265,6 +261,40 @@
}
}
+/**
+ * Renders the user input area, where the user interacts with the UI to enter their credentials.
+ *
+ * For example, this can be the pattern input area, the password text box, or pin pad.
+ */
+@Composable
+private fun UserInputArea(
+ viewModel: BouncerViewModel,
+ modifier: Modifier = Modifier,
+) {
+ val authMethodViewModel: AuthMethodBouncerViewModel? by
+ viewModel.authMethodViewModel.collectAsState()
+
+ when (val nonNullViewModel = authMethodViewModel) {
+ is PinBouncerViewModel ->
+ PinBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ is PasswordBouncerViewModel ->
+ PasswordBouncer(
+ viewModel = nonNullViewModel,
+ modifier = modifier,
+ )
+ is PatternBouncerViewModel ->
+ PatternBouncer(
+ viewModel = nonNullViewModel,
+ modifier =
+ Modifier.aspectRatio(1f, matchHeightConstraintsFirst = false).then(modifier)
+ )
+ else -> Unit
+ }
+}
+
/** Renders the UI of the user switcher that's displayed on large screens next to the bouncer UI. */
@Composable
private fun UserSwitcher(
@@ -287,62 +317,57 @@
)
}
- UserSwitcherDropdown(
- items = dropdownItems,
- )
- }
-}
+ val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
-@Composable
-private fun UserSwitcherDropdown(
- items: List<BouncerViewModel.UserSwitcherDropdownItemViewModel>,
-) {
- val (isDropdownExpanded, setDropdownExpanded) = remember { mutableStateOf(false) }
+ dropdownItems.firstOrNull()?.let { firstDropdownItem ->
+ Spacer(modifier = Modifier.height(40.dp))
- items.firstOrNull()?.let { firstDropdownItem ->
- Spacer(modifier = Modifier.height(40.dp))
+ Box {
+ PlatformButton(
+ modifier =
+ Modifier
+ // Remove the built-in padding applied inside PlatformButton:
+ .padding(vertical = 0.dp)
+ .width(UserSwitcherDropdownWidth)
+ .height(UserSwitcherDropdownHeight),
+ colors =
+ ButtonDefaults.buttonColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
+ contentColor = MaterialTheme.colorScheme.onSurface,
+ ),
+ onClick = { setDropdownExpanded(!isDropdownExpanded) },
+ ) {
+ val context = LocalContext.current
+ Text(
+ text = checkNotNull(firstDropdownItem.text.loadText(context)),
+ style = MaterialTheme.typography.headlineSmall,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis,
+ )
- Box {
- PlatformButton(
- modifier =
- Modifier
- // Remove the built-in padding applied inside PlatformButton:
- .padding(vertical = 0.dp)
- .width(UserSwitcherDropdownWidth)
- .height(UserSwitcherDropdownHeight),
- colors =
- ButtonDefaults.buttonColors(
- containerColor = MaterialTheme.colorScheme.surfaceContainerHighest,
- contentColor = MaterialTheme.colorScheme.onSurface,
- ),
- onClick = { setDropdownExpanded(!isDropdownExpanded) },
- ) {
- val context = LocalContext.current
- Text(
- text = checkNotNull(firstDropdownItem.text.loadText(context)),
- style = MaterialTheme.typography.headlineSmall,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- )
+ Spacer(modifier = Modifier.weight(1f))
- Spacer(modifier = Modifier.weight(1f))
+ Icon(
+ imageVector = Icons.Default.KeyboardArrowDown,
+ contentDescription = null,
+ modifier = Modifier.size(32.dp),
+ )
+ }
- Icon(
- imageVector = Icons.Default.KeyboardArrowDown,
- contentDescription = null,
- modifier = Modifier.size(32.dp),
+ UserSwitcherDropdownMenu(
+ isExpanded = isDropdownExpanded,
+ items = dropdownItems,
+ onDismissed = { setDropdownExpanded(false) },
)
}
-
- UserSwitcherDropdownMenu(
- isExpanded = isDropdownExpanded,
- items = items,
- onDismissed = { setDropdownExpanded(false) },
- )
}
}
}
+/**
+ * Renders the dropdowm menu that displays the actual users and/or user actions that can be
+ * selected.
+ */
@Composable
private fun UserSwitcherDropdownMenu(
isExpanded: Boolean,
@@ -396,19 +421,47 @@
}
/**
- * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
- * anywhere on the background to flip their positions.
+ * Renders the bouncer UI in split mode, with half on one side and half on the other side, swappable
+ * by double-tapping on the side.
*/
@Composable
-private fun SideBySide(
+private fun Split(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
modifier: Modifier = Modifier,
) {
+ SwappableLayout(
+ startContent = { startContentModifier ->
+ Bouncer(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ isUserInputAreaVisible = false,
+ modifier = startContentModifier,
+ )
+ },
+ endContent = { endContentModifier ->
+ UserInputArea(
+ viewModel = viewModel,
+ modifier = endContentModifier,
+ )
+ },
+ modifier = modifier
+ )
+}
+
+/**
+ * Arranges the given two contents side-by-side, supporting a double tap anywhere on the background
+ * to flip their positions.
+ */
+@Composable
+private fun SwappableLayout(
+ startContent: @Composable (Modifier) -> Unit,
+ endContent: @Composable (Modifier) -> Unit,
+ modifier: Modifier = Modifier,
+) {
val layoutDirection = LocalLayoutDirection.current
val isLeftToRight = layoutDirection == LayoutDirection.Ltr
- val (isUserSwitcherFirst, setUserSwitcherFirst) =
- rememberSaveable(isLeftToRight) { mutableStateOf(isLeftToRight) }
+ val (isSwapped, setSwapped) = rememberSaveable(isLeftToRight) { mutableStateOf(!isLeftToRight) }
Row(
modifier =
@@ -416,9 +469,8 @@
detectTapGestures(
onDoubleTap = { offset ->
// Depending on where the user double tapped, switch the elements such that
- // the bouncer contents element is closer to the side that was double
- // tapped.
- setUserSwitcherFirst(offset.x > size.width / 2)
+ // the endContent is closer to the side that was double tapped.
+ setSwapped(offset.x < size.width / 2)
}
)
},
@@ -426,39 +478,30 @@
val animatedOffset by
animateFloatAsState(
targetValue =
- if (isUserSwitcherFirst) {
- // When the user switcher is first, both elements have their natural
- // placement so they are not offset in any way.
+ if (!isSwapped) {
+ // When startContent is first, both elements have their natural placement so
+ // they are not offset in any way.
0f
} else if (isLeftToRight) {
- // Since the user switcher is not first, the elements have to be swapped
- // horizontally. In the case of LTR locales, this means pushing the user
- // switcher to the right, hence the positive number.
+ // Since startContent is not first, the elements have to be swapped
+ // horizontally. In the case of LTR locales, this means pushing startContent
+ // to the right, hence the positive number.
1f
} else {
- // Since the user switcher is not first, the elements have to be swapped
- // horizontally. In the case of RTL locale, this means pushing the user
- // switcher to the left, hence the negative number.
+ // Since startContent is not first, the elements have to be swapped
+ // horizontally. In the case of RTL locales, this means pushing startContent
+ // to the left, hence the negative number.
-1f
},
label = "offset",
)
- val userSwitcherModifier =
+ startContent(
Modifier.fillMaxHeight().weight(1f).graphicsLayer {
translationX = size.width * animatedOffset
alpha = animatedAlpha(animatedOffset)
}
- if (viewModel.isUserSwitcherVisible) {
- UserSwitcher(
- viewModel = viewModel,
- modifier = userSwitcherModifier,
- )
- } else {
- Box(
- modifier = userSwitcherModifier,
- )
- }
+ )
Box(
modifier =
@@ -469,41 +512,124 @@
alpha = animatedAlpha(animatedOffset)
}
) {
- Bouncer(
- viewModel = viewModel,
- dialogFactory = dialogFactory,
- modifier = Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter),
- )
+ endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
}
}
}
-/** Arranges the bouncer contents and user switcher contents one on top of the other. */
+/**
+ * Arranges the bouncer contents and user switcher contents side-by-side, supporting a double tap
+ * anywhere on the background to flip their positions.
+ */
+@Composable
+private fun SideBySide(
+ viewModel: BouncerViewModel,
+ dialogFactory: BouncerSceneDialogFactory,
+ isUserSwitcherVisible: Boolean,
+ modifier: Modifier = Modifier,
+) {
+ SwappableLayout(
+ startContent = { startContentModifier ->
+ if (isUserSwitcherVisible) {
+ UserSwitcher(
+ viewModel = viewModel,
+ modifier = startContentModifier,
+ )
+ } else {
+ Box(
+ modifier = startContentModifier,
+ )
+ }
+ },
+ endContent = { endContentModifier ->
+ Bouncer(
+ viewModel = viewModel,
+ dialogFactory = dialogFactory,
+ isUserInputAreaVisible = true,
+ modifier = endContentModifier,
+ )
+ },
+ modifier = modifier,
+ )
+}
+
+/** Arranges the bouncer contents and user switcher contents one on top of the other, vertically. */
@Composable
private fun Stacked(
viewModel: BouncerViewModel,
dialogFactory: BouncerSceneDialogFactory,
+ isUserSwitcherVisible: Boolean,
modifier: Modifier = Modifier,
) {
Column(
modifier = modifier,
) {
- UserSwitcher(
- viewModel = viewModel,
- modifier = Modifier.fillMaxWidth().weight(1f),
- )
+ if (isUserSwitcherVisible) {
+ UserSwitcher(
+ viewModel = viewModel,
+ modifier = Modifier.fillMaxWidth().weight(1f),
+ )
+ }
+
Bouncer(
viewModel = viewModel,
dialogFactory = dialogFactory,
+ isUserInputAreaVisible = true,
modifier = Modifier.fillMaxWidth().weight(1f),
)
}
}
+@Composable
+private fun calculateLayout(): Layout {
+ val windowSizeClass = LocalWindowSizeClass.current
+ val width = windowSizeClass.widthSizeClass
+ val height = windowSizeClass.heightSizeClass
+ val isLarge = width > WindowWidthSizeClass.Compact && height > WindowHeightSizeClass.Compact
+ val isTall =
+ when (height) {
+ WindowHeightSizeClass.Expanded -> width < WindowWidthSizeClass.Expanded
+ WindowHeightSizeClass.Medium -> width < WindowWidthSizeClass.Medium
+ else -> false
+ }
+ val isSquare =
+ when (width) {
+ WindowWidthSizeClass.Compact -> height == WindowHeightSizeClass.Compact
+ WindowWidthSizeClass.Medium -> height == WindowHeightSizeClass.Medium
+ WindowWidthSizeClass.Expanded -> height == WindowHeightSizeClass.Expanded
+ else -> false
+ }
+ val isLandscape = LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE
+
+ return when {
+ // Small and tall devices (i.e. phone/folded in portrait) or square device not in landscape
+ // mode (unfolded with hinge along horizontal plane).
+ (!isLarge && isTall) || (isSquare && !isLandscape) -> Layout.STANDARD
+ // Small and wide devices (i.e. phone/folded in landscape).
+ !isLarge -> Layout.SPLIT
+ // Large and tall devices (i.e. tablet in portrait).
+ isTall -> Layout.STACKED
+ // Large and wide/square devices (i.e. tablet in landscape, unfolded).
+ else -> Layout.SIDE_BY_SIDE
+ }
+}
+
interface BouncerSceneDialogFactory {
operator fun invoke(): AlertDialog
}
+/** Enumerates all known adaptive layout configurations. */
+private enum class Layout {
+ /** The default UI with the bouncer laid out normally. */
+ STANDARD,
+ /** The bouncer is displayed vertically stacked with the user switcher. */
+ STACKED,
+ /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
+ SIDE_BY_SIDE,
+ /** The bouncer is split in two with both sides shown side-by-side. */
+ SPLIT,
+}
+
/**
* Calculates an alpha for the user switcher and bouncer such that it's at `1` when the offset of
* the two reaches a stopping point but `0` in the middle of the transition.
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
index 2e93a09..0da562b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainer.kt
@@ -161,7 +161,8 @@
fromScene = fromScene.toModel().key,
toScene = toScene.toModel().key,
progress = progress,
- isUserInputDriven = isUserInputDriven,
+ isInitiatedByUserInput = isInitiatedByUserInput,
+ isUserInputOngoing = isUserInputOngoing,
)
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
index 88944f10..199832b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateToScene.kt
@@ -108,7 +108,7 @@
) {
val fromScene = layoutImpl.state.transitionState.currentScene
val isUserInput =
- (layoutImpl.state.transitionState as? TransitionState.Transition)?.isUserInputDriven
+ (layoutImpl.state.transitionState as? TransitionState.Transition)?.isInitiatedByUserInput
?: false
val animationSpec = layoutImpl.transitions.transitionSpec(fromScene, target).spec
@@ -119,9 +119,23 @@
val targetProgress = if (reversed) 0f else 1f
val transition =
if (reversed) {
- OneOffTransition(target, fromScene, currentScene = target, isUserInput, animatable)
+ OneOffTransition(
+ fromScene = target,
+ toScene = fromScene,
+ currentScene = target,
+ isInitiatedByUserInput = isUserInput,
+ isUserInputOngoing = false,
+ animatable = animatable,
+ )
} else {
- OneOffTransition(fromScene, target, currentScene = target, isUserInput, animatable)
+ OneOffTransition(
+ fromScene = fromScene,
+ toScene = target,
+ currentScene = target,
+ isInitiatedByUserInput = isUserInput,
+ isUserInputOngoing = false,
+ animatable = animatable,
+ )
}
// Change the current layout state to use this new transition.
@@ -142,7 +156,8 @@
override val fromScene: SceneKey,
override val toScene: SceneKey,
override val currentScene: SceneKey,
- override val isUserInputDriven: Boolean,
+ override val isInitiatedByUserInput: Boolean,
+ override val isUserInputOngoing: Boolean,
private val animatable: Animatable<Float, AnimationVector1D>,
) : TransitionState.Transition {
override val progress: Float
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
index ccdec6e..1b79dbd 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/ObservableTransitionState.kt
@@ -52,7 +52,14 @@
* scene, this value will remain true after the pointer is no longer touching the screen and
* will be true in any transition created to animate back to the original position.
*/
- val isUserInputDriven: Boolean,
+ val isInitiatedByUserInput: Boolean,
+
+ /**
+ * Whether user input is currently driving the transition. For example, if a user is
+ * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+ * the transition completes/settles.
+ */
+ val isUserInputOngoing: Flow<Boolean>,
) : ObservableTransitionState()
}
@@ -73,7 +80,8 @@
fromScene = state.fromScene,
toScene = state.toScene,
progress = snapshotFlow { state.progress },
- isUserInputDriven = state.isUserInputDriven,
+ isInitiatedByUserInput = state.isInitiatedByUserInput,
+ isUserInputOngoing = snapshotFlow { state.isUserInputOngoing },
)
}
}
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 7a21211..b9f83c5 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
@@ -70,6 +70,9 @@
val progress: Float
/** Whether the transition was triggered by user input rather than being programmatic. */
- val isUserInputDriven: Boolean
+ val isInitiatedByUserInput: Boolean
+
+ /** Whether user input is currently driving the transition. */
+ val isUserInputOngoing: Boolean
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
index 8b79c28..877ac09 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeToScene.kt
@@ -508,7 +508,7 @@
return offset / distance
}
- override val isUserInputDriven = true
+ override val isInitiatedByUserInput = true
/** The current offset caused by the drag gesture. */
var dragOffset by mutableFloatStateOf(0f)
@@ -519,6 +519,10 @@
*/
var isAnimatingOffset by mutableStateOf(false)
+ // If we are not animating offset, it means the offset is being driven by the user's finger.
+ override val isUserInputOngoing: Boolean
+ get() = !isAnimatingOffset
+
/** The animatable used to animate the offset once the user lifted its finger. */
val offsetAnimatable = Animatable(0f, OffsetVisibilityThreshold)
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 4a6066f..58d853e 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
@@ -136,7 +136,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
- assertThat(transition.isUserInputDriven).isTrue()
+ assertThat(transition.isInitiatedByUserInput).isTrue()
// Release the finger. We should now be animating back to A (currentScene = SceneA) given
// that 55dp < positional threshold.
@@ -148,7 +148,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneB)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(55.dp / LayoutWidth)
- assertThat(transition.isUserInputDriven).isTrue()
+ assertThat(transition.isInitiatedByUserInput).isTrue()
// Wait for the animation to finish. We should now be in scene A.
rule.waitForIdle()
@@ -170,7 +170,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneA)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
- assertThat(transition.isUserInputDriven).isTrue()
+ assertThat(transition.isInitiatedByUserInput).isTrue()
// Release the finger. We should now be animating to C (currentScene = SceneC) given
// that 56dp >= positional threshold.
@@ -182,7 +182,7 @@
assertThat(transition.toScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.currentScene).isEqualTo(TestScenes.SceneC)
assertThat(transition.progress).isEqualTo(56.dp / LayoutHeight)
- assertThat(transition.isUserInputDriven).isTrue()
+ assertThat(transition.isInitiatedByUserInput).isTrue()
// Wait for the animation to finish. We should now be in scene C.
rule.waitForIdle()
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 10fd8b5..30392d2 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -34,6 +34,9 @@
<dimen name="navigation_handle_bottom">10dp</dimen>
<dimen name="navigation_handle_sample_horizontal_margin">10dp</dimen>
<dimen name="navigation_home_handle_width">108dp</dimen>
+ <!-- Used while animating the navbar during a long press. -->
+ <dimen name="navigation_home_handle_additional_width_for_animation">20dp</dimen>
+ <dimen name="navigation_home_handle_additional_height_for_animation">4dp</dimen>
<!-- Size of the nav bar edge panels, should be greater to the
edge sensitivity + the drag threshold -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index dc34ef7..26e785d 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -144,5 +144,15 @@
*/
oneway void onStatusBarTrackpadEvent(in MotionEvent event) = 52;
- // Next id = 54
+ /**
+ * Animate the nav bar being long-pressed.
+ *
+ * @param isTouchDown {@code true} if the button is starting to be pressed ({@code false} if
+ * released or canceled)
+ * @param durationMs how long the animation should take (for the {@code isTouchDown} case, this
+ * should be the same as the amount of time to trigger a long-press)
+ */
+ oneway void animateNavBarLongPress(boolean isTouchDown, long durationMs) = 54;
+
+ // Next id = 55
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java
deleted file mode 100644
index aeb0415..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SystemGestureExclusionListenerCompat.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/**
- * Copyright (C) 2019 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.shared.system;
-
-import android.graphics.Region;
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.ISystemGestureExclusionListener;
-import android.view.WindowManagerGlobal;
-
-/**
- * Utility class to listen for exclusion rect changes.
- */
-public abstract class SystemGestureExclusionListenerCompat {
-
- private static final String TAG = "SGEListenerCompat";
-
- private final int mDisplayId;
-
- private ISystemGestureExclusionListener mGestureExclusionListener =
- new ISystemGestureExclusionListener.Stub() {
- @Override
- public void onSystemGestureExclusionChanged(int displayId,
- Region systemGestureExclusion, Region unrestrictedOrNull) {
- if (displayId == mDisplayId) {
- Region unrestricted = (unrestrictedOrNull == null)
- ? systemGestureExclusion : unrestrictedOrNull;
- onExclusionChanged(systemGestureExclusion, unrestricted);
- }
- }
- };
- private boolean mRegistered;
-
- public SystemGestureExclusionListenerCompat(int displayId) {
- mDisplayId = displayId;
- }
-
- /**
- * Called when the exclusion region has changed.
- *
- * TODO: remove, once all subclasses have migrated to
- * {@link #onExclusionChanged(Region, Region)}.
- */
- public abstract void onExclusionChanged(Region systemGestureExclusion);
-
- /**
- * Called when the exclusion region has changed.
- *
- * @param systemGestureExclusion the system gesture exclusion to be applied
- * @param systemGestureExclusionUnrestricted what would be the system gesture exclusion, if
- * there were no restrictions being applied. For logging purposes only.
- *
- */
- public void onExclusionChanged(Region systemGestureExclusion,
- Region systemGestureExclusionUnrestricted) {
- // TODO: make abstract, once all subclasses have migrated away from
- // onExclusionChanged(Region)
- onExclusionChanged(systemGestureExclusion);
- }
-
- /**
- * Registers the listener for getting exclusion rect changes.
- */
- public void register() {
- if (!mRegistered) {
- try {
- WindowManagerGlobal.getWindowManagerService()
- .registerSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
- mRegistered = true;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to register window manager callbacks", e);
- }
- }
- }
-
- /**
- * Unregisters the receiver if previously registered
- */
- public void unregister() {
- if (mRegistered) {
- try {
- WindowManagerGlobal.getWindowManagerService()
- .unregisterSystemGestureExclusionListener(
- mGestureExclusionListener, mDisplayId);
- mRegistered = false;
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to unregister window manager callbacks", e);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/aconfig/AConfigModule.kt b/packages/SystemUI/src/com/android/systemui/aconfig/AConfigModule.kt
deleted file mode 100644
index fa61bba..0000000
--- a/packages/SystemUI/src/com/android/systemui/aconfig/AConfigModule.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.aconfig
-
-import com.android.systemui.FeatureFlags
-import com.android.systemui.FeatureFlagsImpl
-import com.android.systemui.dagger.SysUISingleton
-import dagger.Module
-import dagger.Provides
-
-@Module
-abstract class AConfigModule {
- @Module
- companion object {
- @Provides
- @SysUISingleton
- fun providesImpl(): FeatureFlags {
- return FeatureFlagsImpl()
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index e69eced..1ac4163 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -489,6 +489,10 @@
public void onAttachedToWindow() {
super.onAttachedToWindow();
+ if (mContainerState == STATE_ANIMATING_OUT) {
+ return;
+ }
+
mWakefulnessLifecycle.addObserver(this);
mPanelInteractionDetector.enable(
() -> animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED));
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
index 2bb19cd..f513799 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractor.kt
@@ -26,8 +26,6 @@
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.isDefaultOrientation
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.flags.FeatureFlagsClassic
-import com.android.systemui.flags.Flags
import com.android.systemui.log.SideFpsLogger
import com.android.systemui.res.R
import java.util.Optional
@@ -48,7 +46,6 @@
fingerprintPropertyRepository: FingerprintPropertyRepository,
windowManager: WindowManager,
displayStateInteractor: DisplayStateInteractor,
- featureFlags: FeatureFlagsClassic,
fingerprintInteractiveToAuthProvider: Optional<FingerprintInteractiveToAuthProvider>,
private val logger: SideFpsLogger,
) {
@@ -65,14 +62,11 @@
val isAvailable: Flow<Boolean> =
fingerprintPropertyRepository.sensorType.map { it == FingerprintSensorType.POWER_BUTTON }
- val authenticationDuration: Flow<Long> =
- flowOf(context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L)
+ val authenticationDuration: Long =
+ context.resources?.getInteger(R.integer.config_restToUnlockDuration)?.toLong() ?: 0L
val isProlongedTouchRequiredForAuthentication: Flow<Boolean> =
- if (
- fingerprintInteractiveToAuthProvider.isEmpty ||
- !featureFlags.isEnabled(Flags.REST_TO_UNLOCK)
- ) {
+ if (fingerprintInteractiveToAuthProvider.isEmpty) {
flowOf(false)
} else {
combine(
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
index 9cab17e..5c4ee35 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalRepository.kt
@@ -1,6 +1,6 @@
package com.android.systemui.communal.data.repository
-import com.android.systemui.FeatureFlags
+import com.android.systemui.Flags.communalHub
import com.android.systemui.communal.shared.model.CommunalSceneKey
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
@@ -29,13 +29,10 @@
class CommunalRepositoryImpl
@Inject
constructor(
- private val featureFlags: FeatureFlags,
private val featureFlagsClassic: FeatureFlagsClassic,
) : CommunalRepository {
override val isCommunalEnabled: Boolean
- get() =
- featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) &&
- featureFlags.communalHub()
+ get() = featureFlagsClassic.isEnabled(Flags.COMMUNAL_SERVICE_ENABLED) && communalHub()
private val _desiredScene: MutableStateFlow<CommunalSceneKey> =
MutableStateFlow(CommunalSceneKey.Blank)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 7915088..f3353c7 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -29,7 +29,6 @@
import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.accessibility.AccessibilityModule;
import com.android.systemui.accessibility.data.repository.AccessibilityRepositoryModule;
-import com.android.systemui.aconfig.AConfigModule;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.authentication.AuthenticationModule;
@@ -163,7 +162,6 @@
@Module(includes = {
AccessibilityModule.class,
AccessibilityRepositoryModule.class,
- AConfigModule.class,
AppOpsModule.class,
AssistModule.class,
AuthenticationModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
index 36d789e..87c12b4 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/FeatureFlagsClassicDebug.java
@@ -17,6 +17,7 @@
package com.android.systemui.flags;
import static com.android.systemui.Flags.exampleFlag;
+import static com.android.systemui.Flags.sysuiTeamfood;
import static com.android.systemui.flags.FlagManager.ACTION_GET_FLAGS;
import static com.android.systemui.flags.FlagManager.ACTION_SET_FLAG;
import static com.android.systemui.flags.FlagManager.EXTRA_FLAGS;
@@ -38,13 +39,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.FeatureFlags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.settings.GlobalSettings;
-import org.jetbrains.annotations.NotNull;
-
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Map;
@@ -83,7 +81,6 @@
private final Map<String, Boolean> mBooleanFlagCache = new ConcurrentHashMap<>();
private final Map<String, String> mStringFlagCache = new ConcurrentHashMap<>();
private final Map<String, Integer> mIntFlagCache = new ConcurrentHashMap<>();
- private final FeatureFlags mGantryFlags;
private final Restarter mRestarter;
private final ServerFlagReader.ChangeListener mOnPropertiesChanged =
@@ -128,7 +125,6 @@
@Main Resources resources,
ServerFlagReader serverFlagReader,
@Named(ALL_FLAGS) Map<String, Flag<?>> allFlags,
- FeatureFlags gantryFlags,
Restarter restarter) {
mFlagManager = flagManager;
mContext = context;
@@ -137,7 +133,6 @@
mSystemProperties = systemProperties;
mServerFlagReader = serverFlagReader;
mAllFlags = allFlags;
- mGantryFlags = gantryFlags;
mRestarter = restarter;
}
@@ -155,16 +150,16 @@
}
@Override
- public boolean isEnabled(@NotNull UnreleasedFlag flag) {
+ public boolean isEnabled(@NonNull UnreleasedFlag flag) {
return isEnabledInternal(flag);
}
@Override
- public boolean isEnabled(@NotNull ReleasedFlag flag) {
+ public boolean isEnabled(@NonNull ReleasedFlag flag) {
return isEnabledInternal(flag);
}
- private boolean isEnabledInternal(@NotNull BooleanFlag flag) {
+ private boolean isEnabledInternal(@NonNull BooleanFlag flag) {
String name = flag.getName();
Boolean value = mBooleanFlagCache.get(name);
@@ -266,7 +261,7 @@
&& !defaultValue
&& result == null
&& flag.getTeamfood()) {
- return mGantryFlags.sysuiTeamfood();
+ return sysuiTeamfood();
}
return result == null ? mServerFlagReader.readServerOverride(
@@ -539,7 +534,7 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println("can override: true");
- pw.println("teamfood: " + mGantryFlags.sysuiTeamfood());
+ pw.println("teamfood: " + sysuiTeamfood());
pw.println("booleans: " + mBooleanFlagCache.size());
pw.println("example_flag: " + exampleFlag());
pw.println("example_shared_flag: " + exampleSharedFlag());
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c1106b3..49b8ee6 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -296,11 +296,6 @@
@JvmField val MIGRATE_CLOCKS_TO_BLUEPRINT =
unreleasedFlag("migrate_clocks_to_blueprint")
- /** Migrate KeyguardRootView to use composables. */
- // TODO(b/301969856): Tracking Bug.
- @JvmField val KEYGUARD_ROOT_VIEW_USE_COMPOSE =
- unreleasedFlag("keyguard_root_view_use_compose")
-
/** Enables preview loading animation in the wallpaper picker. */
// TODO(b/274443705): Tracking Bug
@JvmField
@@ -532,10 +527,9 @@
@Keep
@JvmField
val WM_ENABLE_PARTIAL_SCREEN_SHARING_ENTERPRISE_POLICIES =
- unreleasedFlag(
- name = "screen_record_enterprise_policies",
+ releasedFlag(
+ name = "enable_screen_record_enterprise_policies",
namespace = DeviceConfig.NAMESPACE_WINDOW_MANAGER,
- teamfood = false
)
// TODO(b/293252410) : Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
index ba4876f..0bee48a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/SideFpsProgressBarViewBinder.kt
@@ -22,6 +22,8 @@
import com.android.systemui.biometrics.SideFpsController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.view.SideFpsProgressBar
import com.android.systemui.keyguard.ui.viewmodel.SideFpsProgressBarViewModel
import com.android.systemui.log.SideFpsLogger
@@ -31,6 +33,7 @@
import java.io.PrintWriter
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.launch
@@ -47,15 +50,23 @@
private val sfpsController: dagger.Lazy<SideFpsController>,
private val logger: SideFpsLogger,
private val commandRegistry: CommandRegistry,
+ private val featureFlagsClassic: FeatureFlagsClassic,
) : CoreStartable {
override fun start() {
+ if (!featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) {
+ return
+ }
+ // When the rest to unlock feature is disabled by the user, stop any coroutines that are
+ // not required.
+ var layoutJob: Job? = null
+ var progressJob: Job? = null
commandRegistry.registerCommand(spfsProgressBarCommand) { SfpsProgressBarCommand() }
applicationScope.launch {
viewModel.isProlongedTouchRequiredForAuthentication.collectLatest { enabled ->
logger.isProlongedTouchRequiredForAuthenticationChanged(enabled)
if (enabled) {
- launch {
+ layoutJob = launch {
combine(
viewModel.isVisible,
viewModel.progressBarLocation,
@@ -76,9 +87,13 @@
)
}
}
- launch { viewModel.progress.collectLatest { view.setProgress(it) } }
+ progressJob = launch {
+ viewModel.progress.collectLatest { view.setProgress(it) }
+ }
} else {
view.hide()
+ layoutJob?.cancel()
+ progressJob?.cancel()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
index f8996b7..a0f5baf 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/SideFpsProgressBarViewModel.kt
@@ -26,6 +26,8 @@
import com.android.systemui.biometrics.shared.model.isDefaultOrientation
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.keyguard.shared.model.ErrorFingerprintAuthenticationStatus
@@ -34,13 +36,17 @@
import com.android.systemui.res.R
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.onCompletion
+import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
@SysUISingleton
@@ -52,10 +58,12 @@
private val sfpsSensorInteractor: SideFpsSensorInteractor,
displayStateInteractor: DisplayStateInteractor,
@Application private val applicationScope: CoroutineScope,
+ private val featureFlagsClassic: FeatureFlagsClassic,
) {
private val _progress = MutableStateFlow(0.0f)
private val _visible = MutableStateFlow(false)
private var _animator: ValueAnimator? = null
+ private var animatorJob: Job? = null
private fun onFingerprintCaptureCompleted() {
_visible.value = false
@@ -147,26 +155,32 @@
sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication
init {
- applicationScope.launch {
- combine(
- sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication,
- sfpsSensorInteractor.authenticationDuration,
- ::Pair
- )
- .collectLatest { (enabled, authDuration) ->
- if (!enabled) return@collectLatest
+ if (featureFlagsClassic.isEnabled(Flags.REST_TO_UNLOCK)) {
+ launchAnimator()
+ }
+ }
- launch {
- fpAuthRepository.authenticationStatus.collectLatest { authStatus ->
+ private fun launchAnimator() {
+ applicationScope.launch {
+ sfpsSensorInteractor.isProlongedTouchRequiredForAuthentication.collectLatest { enabled
+ ->
+ if (!enabled) {
+ animatorJob?.cancel()
+ return@collectLatest
+ }
+ animatorJob =
+ fpAuthRepository.authenticationStatus
+ .onEach { authStatus ->
when (authStatus) {
is AcquiredFingerprintAuthenticationStatus -> {
if (authStatus.fingerprintCaptureStarted) {
-
_visible.value = true
_animator?.cancel()
_animator =
ValueAnimator.ofFloat(0.0f, 1.0f)
- .setDuration(authDuration)
+ .setDuration(
+ sfpsSensorInteractor.authenticationDuration
+ )
.apply {
addUpdateListener {
_progress.value = it.animatedValue as Float
@@ -196,8 +210,9 @@
else -> Unit
}
}
- }
- }
+ .onCompletion { _animator?.cancel() }
+ .launchIn(applicationScope)
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index ad1c77d..a985236 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -112,7 +112,6 @@
import com.android.internal.util.LatencyTracker;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.Gefingerpoken;
-import com.android.systemui.res.R;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.DisplayId;
@@ -130,6 +129,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
+import com.android.systemui.res.R;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserContextProvider;
import com.android.systemui.settings.UserTracker;
@@ -399,6 +399,11 @@
}
@Override
+ public void animateNavBarLongPress(boolean isTouchDown, long durationMs) {
+ mView.getHomeHandle().animateLongPress(isTouchDown, durationMs);
+ }
+
+ @Override
public void onHomeRotationEnabled(boolean enabled) {
mView.getRotationButtonController().setHomeRotationEnabled(enabled);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
index 10084bd..5fe830e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
@@ -247,6 +247,14 @@
}
}
+ public void animateLongPress(boolean isTouchDown, long durationMs) {
+ for (int i = 0; i < mViews.size(); i++) {
+ if (mViews.get(i) instanceof ButtonInterface) {
+ ((ButtonInterface) mViews.get(i)).animateLongPress(isTouchDown, durationMs);
+ }
+ }
+ }
+
public void setLongClickable(boolean isLongClickable) {
mLongClickable = isLongClickable;
final int N = mViews.size();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java
index 8d291dd..356b2f7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonInterface.java
@@ -30,4 +30,14 @@
void setDarkIntensity(float intensity);
void setDelayTouchFeedback(boolean shouldDelay);
+
+ /**
+ * Animate the button being long-pressed.
+ *
+ * @param isTouchDown {@code true} if the button is starting to be pressed ({@code false} if
+ * released or canceled)
+ * @param durationMs how long the animation should take (for the {@code isTouchDown} case, this
+ * should be the same as the amount of time to trigger a long-press)
+ */
+ default void animateLongPress(boolean isTouchDown, long durationMs) {}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
index 5a22c38..5bfc7dc 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/NavigationHandle.java
@@ -17,6 +17,7 @@
package com.android.systemui.navigationbar.gestural;
import android.animation.ArgbEvaluator;
+import android.animation.ObjectAnimator;
import android.annotation.ColorInt;
import android.content.Context;
import android.content.res.Resources;
@@ -24,12 +25,15 @@
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.FloatProperty;
import android.view.ContextThemeWrapper;
import android.view.View;
+import android.view.animation.Interpolator;
+import com.android.app.animation.Interpolators;
import com.android.settingslib.Utils;
-import com.android.systemui.res.R;
import com.android.systemui.navigationbar.buttons.ButtonInterface;
+import com.android.systemui.res.R;
public class NavigationHandle extends View implements ButtonInterface {
@@ -38,8 +42,26 @@
private @ColorInt final int mDarkColor;
protected final float mRadius;
protected final float mBottom;
+ private final float mAdditionalWidthForAnimation;
+ private final float mAdditionalHeightForAnimation;
private boolean mRequiresInvalidate;
+ private ObjectAnimator mPulseAnimator = null;
+ private float mPulseAnimationProgress;
+
+ private static final FloatProperty<NavigationHandle> PULSE_ANIMATION_PROGRESS =
+ new FloatProperty<>("pulseAnimationProgress") {
+ @Override
+ public Float get(NavigationHandle controller) {
+ return controller.getPulseAnimationProgress();
+ }
+
+ @Override
+ public void setValue(NavigationHandle controller, float progress) {
+ controller.setPulseAnimationProgress(progress);
+ }
+ };
+
public NavigationHandle(Context context) {
this(context, null);
}
@@ -49,6 +71,10 @@
final Resources res = context.getResources();
mRadius = res.getDimension(R.dimen.navigation_handle_radius);
mBottom = res.getDimension(R.dimen.navigation_handle_bottom);
+ mAdditionalWidthForAnimation =
+ res.getDimension(R.dimen.navigation_home_handle_additional_width_for_animation);
+ mAdditionalHeightForAnimation =
+ res.getDimension(R.dimen.navigation_home_handle_additional_height_for_animation);
final int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme);
final int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme);
@@ -75,23 +101,24 @@
// Draw that bar
int navHeight = getHeight();
- float height = mRadius * 2;
- int width = getWidth();
- float y = (navHeight - mBottom - height);
- canvas.drawRoundRect(0, y, width, y + height, mRadius, mRadius, mPaint);
+ float additionalHeight = mAdditionalHeightForAnimation * mPulseAnimationProgress;
+ float height = mRadius * 2 + additionalHeight;
+ float additionalWidth = mAdditionalWidthForAnimation * mPulseAnimationProgress;
+ float width = getWidth() + additionalWidth;
+ float x = -(additionalWidth / 2);
+ float y = navHeight - mBottom - height - (additionalHeight / 2);
+ float adjustedRadius = height / 2;
+ canvas.drawRoundRect(x, y, width, y + height, adjustedRadius, adjustedRadius, mPaint);
}
@Override
- public void setImageDrawable(Drawable drawable) {
- }
+ public void setImageDrawable(Drawable drawable) {}
@Override
- public void abortCurrentGesture() {
- }
+ public void abortCurrentGesture() {}
@Override
- public void setVertical(boolean vertical) {
- }
+ public void setVertical(boolean vertical) {}
@Override
public void setDarkIntensity(float intensity) {
@@ -108,6 +135,43 @@
}
@Override
- public void setDelayTouchFeedback(boolean shouldDelay) {
+ public void setDelayTouchFeedback(boolean shouldDelay) {}
+
+ @Override
+ public void animateLongPress(boolean isTouchDown, long durationMs) {
+ if (mPulseAnimator != null) {
+ mPulseAnimator.cancel();
+ }
+
+ Interpolator interpolator;
+ if (isTouchDown) {
+ // For now we animate the navbar expanding and contracting so that the navbar is the
+ // original size by the end of {@code duration}. This is because a screenshot is taken
+ // at that point and we don't want to capture the larger navbar.
+ // TODO(b/306400785): Determine a way to exclude navbar from the screenshot.
+
+ // Fraction of the touch down animation to expand; remaining is used to contract again.
+ float expandFraction = 0.9f;
+ interpolator = t -> t <= expandFraction
+ ? Interpolators.clampToProgress(Interpolators.LEGACY, t, 0, expandFraction)
+ : 1 - Interpolators.clampToProgress(
+ Interpolators.LINEAR, t, expandFraction, 1);
+ } else {
+ interpolator = Interpolators.LEGACY_DECELERATE;
+ }
+
+ mPulseAnimator =
+ ObjectAnimator.ofFloat(this, PULSE_ANIMATION_PROGRESS, isTouchDown ? 1 : 0);
+ mPulseAnimator.setDuration(durationMs).setInterpolator(interpolator);
+ mPulseAnimator.start();
+ }
+
+ private void setPulseAnimationProgress(float pulseAnimationProgress) {
+ mPulseAnimationProgress = pulseAnimationProgress;
+ invalidate();
+ }
+
+ private float getPulseAnimationProgress() {
+ return mPulseAnimationProgress;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 4465504..1334660 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -245,6 +245,12 @@
}
@Override
+ public void animateNavBarLongPress(boolean isTouchDown, long durationMs) {
+ verifyCallerAndClearCallingIdentityPostMain("animateNavBarLongPress", () ->
+ notifyAnimateNavBarLongPress(isTouchDown, durationMs));
+ }
+
+ @Override
public void onBackPressed() {
verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
@@ -914,6 +920,12 @@
}
}
+ private void notifyAnimateNavBarLongPress(boolean isTouchDown, long durationMs) {
+ for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
+ mConnectionCallbacks.get(i).animateNavBarLongPress(isTouchDown, durationMs);
+ }
+ }
+
public void notifyAssistantVisibilityChanged(float visibility) {
try {
if (mOverviewProxy != null) {
@@ -1058,6 +1070,7 @@
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
default void setAssistantOverridesRequested(int[] invocationTypes) {}
+ default void animateNavBarLongPress(boolean isTouchDown, long durationMs) {}
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
index 7353379..b144003 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/interactor/SceneInteractor.kt
@@ -26,9 +26,12 @@
import com.android.systemui.scene.shared.model.SceneModel
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.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -105,6 +108,26 @@
initialValue = null,
)
+ /**
+ * Whether user input is ongoing for the current transition. For example, if the user is swiping
+ * their finger to transition between scenes, this value will be true while their finger is on
+ * the screen, then false for the rest of the transition.
+ */
+ @OptIn(ExperimentalCoroutinesApi::class)
+ val isTransitionUserInputOngoing: StateFlow<Boolean> =
+ transitionState
+ .flatMapLatest {
+ when (it) {
+ is ObservableTransitionState.Transition -> it.isUserInputOngoing
+ is ObservableTransitionState.Idle -> flowOf(false)
+ }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = false
+ )
+
/** Whether the scene container is visible. */
val isVisible: StateFlow<Boolean> = repository.isVisible
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
index 9ba02b1..49bceef 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/flag/SceneContainerFlags.kt
@@ -17,8 +17,8 @@
package com.android.systemui.scene.shared.flag
import androidx.annotation.VisibleForTesting
-import com.android.systemui.FeatureFlags
import com.android.systemui.Flags as AConfigFlags
+import com.android.systemui.Flags.sceneContainer
import com.android.systemui.compose.ComposeFacade
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlagsClassic
@@ -50,7 +50,6 @@
@AssistedInject
constructor(
private val featureFlagsClassic: FeatureFlagsClassic,
- featureFlags: FeatureFlags,
@Assisted private val isComposeAvailable: Boolean,
) : SceneContainerFlags {
@@ -72,7 +71,7 @@
listOf(
AconfigFlagMustBeEnabled(
flagName = AConfigFlags.FLAG_SCENE_CONTAINER,
- flagValue = featureFlags.sceneContainer(),
+ flagValue = sceneContainer(),
),
) +
classicFlagTokens.map { flagToken -> FlagMustBeEnabled(flagToken) } +
diff --git a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
index 3927873..f704894 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/shared/model/ObservableTransitionState.kt
@@ -42,6 +42,13 @@
* scene, this value will remain true after the pointer is no longer touching the screen and
* will be true in any transition created to animate back to the original position.
*/
- val isUserInputDriven: Boolean,
+ val isInitiatedByUserInput: Boolean,
+
+ /**
+ * Whether user input is currently driving the transition. For example, if a user is
+ * dragging a pointer, this emits true. Once they lift their finger, this emits false while
+ * the transition completes/settles.
+ */
+ val isUserInputOngoing: Flow<Boolean>,
) : ObservableTransitionState()
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index a4c4503..b2ffeb3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -275,7 +275,7 @@
when (state) {
is ObservableTransitionState.Idle -> false
is ObservableTransitionState.Transition ->
- state.isUserInputDriven &&
+ state.isInitiatedByUserInput &&
(state.toScene == sceneKey || state.fromScene == sceneKey)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
index fda4133..225f125 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.kt
@@ -810,6 +810,7 @@
SceneKey.Bouncer,
flowOf(.5f),
false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -825,7 +826,8 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false
+ false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -842,7 +844,8 @@
SceneKey.Gone,
SceneKey.Bouncer,
flowOf(.5f),
- false
+ false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Bouncer, null), "reason")
@@ -860,7 +863,8 @@
SceneKey.Bouncer,
SceneKey.Gone,
flowOf(.5f),
- false
+ false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
@@ -876,6 +880,7 @@
SceneKey.Lockscreen,
flowOf(.5f),
false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Lockscreen, null), "reason")
@@ -893,6 +898,7 @@
SceneKey.Gone,
flowOf(.5f),
false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone, null), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
index 0b38c4a..837a130 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationAnimationControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -29,7 +31,6 @@
import android.animation.ValueAnimator;
import android.annotation.Nullable;
-import android.app.Instrumentation;
import android.content.Context;
import android.graphics.Rect;
import android.os.Handler;
@@ -42,7 +43,6 @@
import android.view.accessibility.IRemoteMagnificationAnimationCallback;
import android.view.animation.AccelerateInterpolator;
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
@@ -103,7 +103,7 @@
private SpyWindowMagnificationController mController;
private WindowMagnificationController mSpyController;
private WindowMagnificationAnimationController mWindowMagnificationAnimationController;
- private Instrumentation mInstrumentation;
+
private long mWaitAnimationDuration;
private long mWaitPartialAnimationDuration;
@@ -113,7 +113,6 @@
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mInstrumentation = InstrumentationRegistry.getInstrumentation();
final WindowManager wm = mContext.getSystemService(WindowManager.class);
mWindowManager = spy(new TestableWindowManager(wm));
mContext.addMockSystemService(Context.WINDOW_SERVICE, mWindowManager);
@@ -135,7 +134,7 @@
@After
public void tearDown() throws Exception {
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mController.deleteWindowMagnification();
});
}
@@ -199,7 +198,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
resetMockObjects();
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
@@ -252,7 +251,7 @@
final float targetCenterY = mirrorView.getHeight() / 2.0f;
Mockito.reset(mSpyController);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback);
mCurrentScale.set(mController.getScale());
@@ -288,7 +287,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback);
mCurrentScale.set(mController.getScale());
@@ -376,7 +375,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
@@ -388,7 +387,7 @@
verify(mAnimationCallback2, never()).onResult(anyBoolean());
verify(mAnimationCallback).onResult(false);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
// ValueAnimator.reverse() could not work correctly with the AnimatorTestRule since it
// is using SystemClock in reverse() (b/305731398). Therefore, we call end() on the
// animator directly to verify the result of animation is correct instead of querying
@@ -456,7 +455,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
Mockito.reset(mSpyController);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(targetScale,
targetCenterX, targetCenterY, mAnimationCallback2);
mCurrentScale.set(mController.getScale());
@@ -484,7 +483,7 @@
final Rect windowBounds = new Rect(mWindowManager.getCurrentWindowMetrics().getBounds());
Mockito.reset(mSpyController);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(DEFAULT_SCALE,
windowBounds.exactCenterX(), windowBounds.exactCenterY(),
offsetRatio, offsetRatio, mAnimationCallback);
@@ -512,7 +511,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
enableWindowMagnificationWithoutAnimation();
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
targetCenterX, targetCenterY, mAnimationCallback);
advanceTimeBy(mWaitAnimationDuration);
@@ -528,7 +527,7 @@
throws RemoteException {
enableWindowMagnificationWithoutAnimation();
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
DEFAULT_CENTER_X + 10, DEFAULT_CENTER_Y + 10, mAnimationCallback);
mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
@@ -556,7 +555,7 @@
enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
mAnimationCallback);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
targetCenterX, targetCenterY, mAnimationCallback2);
advanceTimeBy(mWaitAnimationDuration);
@@ -576,7 +575,7 @@
enableWindowMagnificationAndWaitAnimating(mWaitPartialAnimationDuration,
mAnimationCallback);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.moveWindowMagnifierToPosition(
Float.NaN, Float.NaN, mAnimationCallback2);
advanceTimeBy(mWaitAnimationDuration);
@@ -648,7 +647,7 @@
mAnimationCallback);
Mockito.reset(mSpyController);
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.deleteWindowMagnification(
mAnimationCallback2);
mCurrentScale.set(mController.getScale());
@@ -736,7 +735,7 @@
final float offsetY =
(float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
+ 1.0f;
- mInstrumentation.runOnMainSync(()-> mController.moveWindowMagnifier(offsetX, offsetY));
+ getInstrumentation().runOnMainSync(()-> mController.moveWindowMagnifier(offsetX, offsetY));
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
verifyFinalSpec(DEFAULT_SCALE, DEFAULT_CENTER_X, DEFAULT_CENTER_Y + offsetY);
@@ -751,7 +750,7 @@
final float offsetY =
(float) Math.floor(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE)
- 1.0f;
- mInstrumentation.runOnMainSync(() ->
+ getInstrumentation().runOnMainSync(() ->
mController.moveWindowMagnifier(offsetX, offsetY));
verify(mSpyController).moveWindowMagnifier(offsetX, offsetY);
@@ -767,7 +766,7 @@
(float) Math.ceil(offsetX * WindowMagnificationController.HORIZONTAL_LOCK_BASE);
// while diagonal scrolling enabled,
// should move with both offsetX and offsetY without regrading offsetY/offsetX
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mController.setDiagonalScrolling(true);
mController.moveWindowMagnifier(offsetX, offsetY);
});
@@ -782,7 +781,7 @@
final float targetCenterY = DEFAULT_CENTER_Y + 100;
enableWindowMagnificationWithoutAnimation();
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mController.moveWindowMagnifierToPosition(targetCenterX, targetCenterY,
mAnimationCallback);
advanceTimeBy(mWaitAnimationDuration);
@@ -809,7 +808,7 @@
private void enableWindowMagnificationWithoutAnimation(
float targetScale, float targetCenterX, float targetCenterY) {
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(
targetScale, targetCenterX, targetCenterY, null);
});
@@ -827,7 +826,7 @@
float targetCenterX,
float targetCenterY,
@Nullable IRemoteMagnificationAnimationCallback callback) {
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.enableWindowMagnification(
targetScale, targetCenterX, targetCenterY, callback);
advanceTimeBy(duration);
@@ -835,14 +834,14 @@
}
private void deleteWindowMagnificationWithoutAnimation() {
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.deleteWindowMagnification(null);
});
}
private void deleteWindowMagnificationAndWaitAnimating(long duration,
@Nullable IRemoteMagnificationAnimationCallback callback) {
- mInstrumentation.runOnMainSync(() -> {
+ getInstrumentation().runOnMainSync(() -> {
mWindowMagnificationAnimationController.deleteWindowMagnification(callback);
advanceTimeBy(duration);
});
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 5e7b857..2d95b09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -54,6 +54,7 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.VibratorHelper
+import com.android.systemui.statusbar.events.ANIMATING_OUT
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
@@ -211,6 +212,16 @@
}
@Test
+ fun testIgnoresAnimatedInWhenDialogAnimatingOut() {
+ val container = initializeFingerprintContainer(addToView = false)
+ container.mContainerState = ANIMATING_OUT
+ container.addToView()
+ waitForIdleSync()
+
+ verify(callback, never()).onDialogAnimatedIn(anyLong(), anyBoolean())
+ }
+
+ @Test
fun testDismissBeforeIntroEnd() {
val container = initializeFingerprintContainer()
waitForIdleSync()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
index 3fbdeec..67d3a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/SideFpsSensorInteractorTest.kt
@@ -37,8 +37,6 @@
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.flags.FakeFeatureFlagsClassic
-import com.android.systemui.flags.Flags.REST_TO_UNLOCK
import com.android.systemui.log.SideFpsLogger
import com.android.systemui.res.R
import com.android.systemui.util.mockito.whenever
@@ -94,7 +92,6 @@
whenever(displayStateInteractor.currentRotation).thenReturn(currentRotation)
contextDisplayInfo.uniqueId = "current-display"
- val featureFlags = FakeFeatureFlagsClassic().apply { set(REST_TO_UNLOCK, true) }
whenever(fingerprintInteractiveToAuthProvider.enabledForCurrentUser)
.thenReturn(isRestToUnlockEnabled)
underTest =
@@ -103,7 +100,6 @@
fingerprintRepository,
windowManager,
displayStateInteractor,
- featureFlags,
Optional.of(fingerprintInteractiveToAuthProvider),
SideFpsLogger(logcatLogBuffer("SfpsLogger"))
)
@@ -136,7 +132,7 @@
@Test
fun authenticationDurationIsAvailableWhenSFPSSensorIsAvailable() =
testScope.runTest {
- assertThat(collectLastValue(underTest.authenticationDuration)())
+ assertThat(underTest.authenticationDuration)
.isEqualTo(context.resources.getInteger(R.integer.config_restToUnlockDuration))
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
index f51745b..b589a2a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/flags/FeatureFlagsClassicDebugTest.kt
@@ -22,7 +22,6 @@
import android.content.res.Resources
import android.content.res.Resources.NotFoundException
import android.test.suitebuilder.annotation.SmallTest
-import com.android.systemui.FakeFeatureFlagsImpl
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -66,7 +65,6 @@
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var clearCacheAction: Consumer<String>
private val serverFlagReader = ServerFlagReaderFake()
- private val fakeGantryFlags = FakeFeatureFlagsImpl()
private val teamfoodableFlagA = UnreleasedFlag(name = "a", namespace = "test", teamfood = true)
private val teamfoodableFlagB = ReleasedFlag(name = "b", namespace = "test", teamfood = true)
@@ -74,7 +72,6 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", false)
flagMap.put(teamfoodableFlagA.name, teamfoodableFlagA)
flagMap.put(teamfoodableFlagB.name, teamfoodableFlagB)
mFeatureFlagsClassicDebug =
@@ -86,7 +83,6 @@
resources,
serverFlagReader,
flagMap,
- fakeGantryFlags,
restarter
)
mFeatureFlagsClassicDebug.init()
@@ -134,7 +130,7 @@
@Test
fun teamFoodFlag_True() {
- fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", true)
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isTrue()
@@ -149,7 +145,7 @@
.thenReturn(true)
whenever(flagManager.readFlagValue<Boolean>(eq(teamfoodableFlagB.name), any()))
.thenReturn(false)
- fakeGantryFlags.setFlag("com.android.systemui.sysui_teamfood", true)
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_SYSUI_TEAMFOOD)
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagA)).isTrue()
assertThat(mFeatureFlagsClassicDebug.isEnabled(teamfoodableFlagB)).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 27325d3..ad2ec72 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -203,7 +203,8 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
assertThat(isAnimate).isFalse()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 88a5c17..9c0456c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -54,6 +54,7 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -466,7 +467,8 @@
fromScene = getCurrentSceneInUi(),
toScene = to.key,
progress = progressFlow,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
index 456f1bc..d669006 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/data/repository/SceneContainerRepositoryTest.kt
@@ -29,6 +29,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -120,7 +121,8 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
index 8b23d18..3f032a4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/interactor/SceneInteractorTest.kt
@@ -83,7 +83,8 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(reflectedTransitionState).isEqualTo(transitionState.value)
@@ -121,7 +122,8 @@
fromScene = underTest.desiredScene.value.key,
toScene = SceneKey.Shade,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(transitionTo).isEqualTo(SceneKey.Shade)
@@ -158,7 +160,8 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
val transitioning by
@@ -177,7 +180,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
underTest.setTransitionState(transitionState)
@@ -194,7 +198,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
val transitioning by
@@ -222,7 +227,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Lockscreen,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(transitioning).isTrue()
@@ -231,6 +237,95 @@
}
@Test
+ fun isTransitionUserInputOngoing_idle_false() =
+ testScope.runTest {
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Idle(SceneKey.Shade)
+ )
+ val isTransitionUserInputOngoing by
+ collectLastValue(underTest.isTransitionUserInputOngoing)
+ underTest.setTransitionState(transitionState)
+
+ assertThat(isTransitionUserInputOngoing).isFalse()
+ }
+
+ @Test
+ fun isTransitionUserInputOngoing_transition_true() =
+ testScope.runTest {
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Shade,
+ toScene = SceneKey.Lockscreen,
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ )
+ val isTransitionUserInputOngoing by
+ collectLastValue(underTest.isTransitionUserInputOngoing)
+ underTest.setTransitionState(transitionState)
+
+ assertThat(isTransitionUserInputOngoing).isTrue()
+ }
+
+ @Test
+ fun isTransitionUserInputOngoing_updateMidTransition_false() =
+ testScope.runTest {
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Shade,
+ toScene = SceneKey.Lockscreen,
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ )
+ val isTransitionUserInputOngoing by
+ collectLastValue(underTest.isTransitionUserInputOngoing)
+ underTest.setTransitionState(transitionState)
+
+ assertThat(isTransitionUserInputOngoing).isTrue()
+
+ transitionState.value =
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Shade,
+ toScene = SceneKey.Lockscreen,
+ progress = flowOf(0.6f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
+ )
+
+ assertThat(isTransitionUserInputOngoing).isFalse()
+ }
+
+ @Test
+ fun isTransitionUserInputOngoing_updateOnIdle_false() =
+ testScope.runTest {
+ val transitionState =
+ MutableStateFlow<ObservableTransitionState>(
+ ObservableTransitionState.Transition(
+ fromScene = SceneKey.Shade,
+ toScene = SceneKey.Lockscreen,
+ progress = flowOf(0.5f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(true),
+ )
+ )
+ val isTransitionUserInputOngoing by
+ collectLastValue(underTest.isTransitionUserInputOngoing)
+ underTest.setTransitionState(transitionState)
+
+ assertThat(isTransitionUserInputOngoing).isTrue()
+
+ transitionState.value = ObservableTransitionState.Idle(scene = SceneKey.Lockscreen)
+
+ assertThat(isTransitionUserInputOngoing).isFalse()
+ }
+
+ @Test
fun isVisible() =
testScope.runTest {
val isVisible by collectLastValue(underTest.isVisible)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index c1f2d0c..c0b5861 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -115,7 +115,8 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Shade), "reason")
@@ -128,7 +129,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.Gone,
progress = flowOf(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
assertThat(isVisible).isTrue()
sceneInteractor.onSceneChanged(SceneModel(SceneKey.Gone), "reason")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
index 0bed4d0..32a38bd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/shared/flag/SceneContainerFlagsTest.kt
@@ -76,7 +76,6 @@
underTest =
SceneContainerFlagsImpl(
featureFlagsClassic = featureFlags,
- featureFlags = aconfigFlags,
isComposeAvailable = testCase.isComposeAvailable,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 3a260ae..8b8a625 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -60,6 +60,7 @@
import dagger.Component
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -495,7 +496,8 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -532,7 +534,8 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -568,7 +571,8 @@
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.Shade,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -844,7 +848,8 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -881,7 +886,8 @@
fromScene = SceneKey.Lockscreen,
toScene = key,
progress = progress,
- isUserInputDriven = true,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -918,7 +924,8 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -955,7 +962,8 @@
fromScene = key,
toScene = SceneKey.Lockscreen,
progress = progress,
- isUserInputDriven = true,
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
@@ -990,8 +998,9 @@
ObservableTransitionState.Transition(
fromScene = SceneKey.Lockscreen,
toScene = SceneKey.QuickSettings,
- progress = progress,
- isUserInputDriven = true,
+ progress = MutableStateFlow(0f),
+ isInitiatedByUserInput = true,
+ isUserInputOngoing = flowOf(false),
)
)
sceneInteractor.setTransitionState(transitionState)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
index f98267b..02d15de 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt
@@ -20,6 +20,7 @@
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -89,7 +90,8 @@
fromScene = SceneKey.Shade,
toScene = SceneKey.QuickSettings,
progress = MutableStateFlow(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
)
@@ -107,7 +109,8 @@
fromScene = SceneKey.QuickSettings,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
)
@@ -125,7 +128,8 @@
fromScene = SceneKey.Gone,
toScene = SceneKey.Shade,
progress = MutableStateFlow(0.5f),
- isUserInputDriven = false,
+ isInitiatedByUserInput = false,
+ isUserInputOngoing = flowOf(false),
)
)
)
diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig
index cbaf05b..a770b66 100644
--- a/services/core/java/com/android/server/am/flags.aconfig
+++ b/services/core/java/com/android/server/am/flags.aconfig
@@ -22,3 +22,10 @@
description: "Detect abusive FGS behavior for certain types (camera, mic, media, location)."
bug: "295545575"
}
+
+flag {
+ name: "fgs_boot_completed"
+ namespace: "backstage_power"
+ description: "Disable BOOT_COMPLETED broadcast FGS start for certain types"
+ bug: "296558535"
+}
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index a6558e0..eea13f1 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -14575,9 +14575,10 @@
mModStepMode = 0;
}
} else {
- if (level >= 90) {
- // If the battery level is at least 90%, always consider the device to be
- // charging even if it happens to go down a level.
+ if (level >= mConstants.BATTERY_CHARGING_ENFORCE_LEVEL) {
+ // If the battery level is at least Constants.BATTERY_CHARGING_ENFORCE_LEVEL,
+ // always consider the device to be charging even if it happens to go down a
+ // level.
changed |= setChargingLocked(true);
} else if (!mCharging) {
if (mLastChargeStepLevel < level) {
@@ -15313,6 +15314,8 @@
public static final String KEY_MAX_HISTORY_BUFFER_KB = "max_history_buffer_kb";
public static final String KEY_BATTERY_CHARGED_DELAY_MS =
"battery_charged_delay_ms";
+ public static final String KEY_BATTERY_CHARGING_ENFORCE_LEVEL =
+ "battery_charging_enforce_level";
public static final String KEY_PER_UID_MODEM_POWER_MODEL =
"per_uid_modem_power_model";
public static final String KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION =
@@ -15363,6 +15366,7 @@
private static final int DEFAULT_MAX_HISTORY_FILES_LOW_RAM_DEVICE = 64;
private static final int DEFAULT_MAX_HISTORY_BUFFER_LOW_RAM_DEVICE_KB = 64; /*Kilo Bytes*/
private static final int DEFAULT_BATTERY_CHARGED_DELAY_MS = 900000; /* 15 min */
+ private static final int DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL = 90;
@PerUidModemPowerModel
private static final int DEFAULT_PER_UID_MODEM_MODEL =
PER_UID_MODEM_POWER_MODEL_MODEM_ACTIVITY_INFO_RX_TX;
@@ -15384,6 +15388,7 @@
public int MAX_HISTORY_FILES;
public int MAX_HISTORY_BUFFER; /*Bytes*/
public int BATTERY_CHARGED_DELAY_MS = DEFAULT_BATTERY_CHARGED_DELAY_MS;
+ public int BATTERY_CHARGING_ENFORCE_LEVEL = DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL;
public int PER_UID_MODEM_MODEL = DEFAULT_PER_UID_MODEM_MODEL;
public boolean PHONE_ON_EXTERNAL_STATS_COLLECTION =
DEFAULT_PHONE_ON_EXTERNAL_STATS_COLLECTION;
@@ -15412,6 +15417,9 @@
mResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.BATTERY_CHARGING_STATE_UPDATE_DELAY),
false /* notifyForDescendants */, this);
+ mResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL),
+ false /* notifyForDescendants */, this);
updateConstants();
}
@@ -15424,6 +15432,12 @@
updateBatteryChargedDelayMsLocked();
}
return;
+ } else if (uri.equals(Settings.Global.getUriFor(
+ Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL))) {
+ synchronized (BatteryStatsImpl.this) {
+ updateBatteryChargingEnforceLevelLocked();
+ }
+ return;
}
updateConstants();
}
@@ -15477,6 +15491,7 @@
DEFAULT_RESET_WHILE_PLUGGED_IN_MINIMUM_DURATION_HOURS);
updateBatteryChargedDelayMsLocked();
+ updateBatteryChargingEnforceLevelLocked();
onChange();
}
@@ -15507,6 +15522,21 @@
}
}
+ private void updateBatteryChargingEnforceLevelLocked() {
+ int lastChargingEnforceLevel = BATTERY_CHARGING_ENFORCE_LEVEL;
+ final int level = Settings.Global.getInt(mResolver,
+ Settings.Global.BATTERY_CHARGING_STATE_ENFORCE_LEVEL,
+ -1);
+
+ BATTERY_CHARGING_ENFORCE_LEVEL = level >= 0 ? level : mParser.getInt(
+ KEY_BATTERY_CHARGING_ENFORCE_LEVEL, DEFAULT_BATTERY_CHARGING_ENFORCE_LEVEL);
+
+ if (BATTERY_CHARGING_ENFORCE_LEVEL <= mLastChargeStepLevel
+ && mLastChargeStepLevel < lastChargingEnforceLevel) {
+ setChargingLocked(true);
+ }
+ }
+
private void updateKernelUidReadersThrottleTime(long oldTimeMs, long newTimeMs) {
KERNEL_UID_READERS_THROTTLE_TIME = newTimeMs;
if (oldTimeMs != newTimeMs) {
@@ -15541,6 +15571,8 @@
pw.println(MAX_HISTORY_BUFFER/1024);
pw.print(KEY_BATTERY_CHARGED_DELAY_MS); pw.print("=");
pw.println(BATTERY_CHARGED_DELAY_MS);
+ pw.print(KEY_BATTERY_CHARGING_ENFORCE_LEVEL); pw.print("=");
+ pw.println(BATTERY_CHARGING_ENFORCE_LEVEL);
pw.print(KEY_PER_UID_MODEM_POWER_MODEL); pw.print("=");
pw.println(getPerUidModemModelName(PER_UID_MODEM_MODEL));
pw.print(KEY_PHONE_ON_EXTERNAL_STATS_COLLECTION); pw.print("=");
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index 4ebd402..5fd787a 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -126,10 +126,10 @@
connection = new StorageUserConnection(mContext, userId, this);
mConnections.put(userId, connection);
}
- Slog.i(TAG, "Creating and starting session with id: " + sessionId);
- connection.startSession(sessionId, deviceFd, vol.getPath().getPath(),
- vol.getInternalPath().getPath());
}
+ Slog.i(TAG, "Creating and starting session with id: " + sessionId);
+ connection.startSession(sessionId, deviceFd, vol.getPath().getPath(),
+ vol.getInternalPath().getPath());
}
/**
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index b428ed2..d6637e1 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2448,7 +2448,7 @@
final int typeParameter = StartingSurfaceController
.makeStartingWindowTypeParameter(newTask, taskSwitch, processRunning,
allowTaskSnapshot, activityCreated, isSimple, useLegacy, activityAllDrawn,
- type, packageName, mUserId);
+ type, isIconStylePreferred(resolvedTheme), packageName, mUserId);
if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
if (isActivityTypeHome()) {
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 668cd87..68082df 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -75,6 +75,7 @@
private static final long ASM_GRACEPERIOD_TIMEOUT_MS = TIMEOUT_MS;
private static final int ASM_GRACEPERIOD_MAX_REPEATS = 5;
+ private static final int NO_PROCESS_UID = -1;
public static final ActivityOptions ACTIVITY_OPTIONS_SYSTEM_DEFINED =
ActivityOptions.makeBasic()
.setPendingIntentBackgroundActivityStartMode(
@@ -649,7 +650,7 @@
if (PendingIntentRecord.isPendingIntentBalAllowedByPermission(checkedOptions)
&& ActivityManager.checkComponentPermission(
android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND,
- state.mRealCallingUid, -1, true) == PackageManager.PERMISSION_GRANTED) {
+ state.mRealCallingUid, NO_PROCESS_UID, true) == PackageManager.PERMISSION_GRANTED) {
return new BalVerdict(BAL_ALLOW_PENDING_INTENT,
/*background*/ false,
"realCallingUid has BAL permission.");
@@ -834,7 +835,7 @@
/* caller_activity_class_name */
sourceRecord != null ? sourceRecord.info.name : null,
/* target_task_top_activity_uid */
- targetTopActivity != null ? targetTopActivity.getUid() : -1,
+ targetTopActivity != null ? targetTopActivity.getUid() : NO_PROCESS_UID,
/* target_task_top_activity_class_name */
targetTopActivity != null ? targetTopActivity.info.name : null,
/* target_task_is_different */
@@ -988,13 +989,13 @@
/* caller_activity_class_name */
callerActivityClassName,
/* target_task_top_activity_uid */
- topActivity == null ? -1 : topActivity.getUid(),
+ topActivity == null ? NO_PROCESS_UID : topActivity.getUid(),
/* target_task_top_activity_class_name */
topActivity == null ? null : topActivity.info.name,
/* target_task_is_different */
false,
/* target_activity_uid */
- -1,
+ NO_PROCESS_UID,
/* target_activity_class_name */
null,
/* target_intent_action */
diff --git a/services/core/java/com/android/server/wm/StartingSurfaceController.java b/services/core/java/com/android/server/wm/StartingSurfaceController.java
index a55c232..28a35b9 100644
--- a/services/core/java/com/android/server/wm/StartingSurfaceController.java
+++ b/services/core/java/com/android/server/wm/StartingSurfaceController.java
@@ -20,6 +20,7 @@
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ACTIVITY_DRAWN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_ALLOW_TASK_SNAPSHOT;
+import static android.window.StartingWindowInfo.TYPE_PARAMETER_APP_PREFERS_ICON;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_LEGACY_SPLASH_SCREEN;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_NEW_TASK;
import static android.window.StartingWindowInfo.TYPE_PARAMETER_PROCESS_RUNNING;
@@ -103,7 +104,7 @@
static int makeStartingWindowTypeParameter(boolean newTask, boolean taskSwitch,
boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated,
boolean isSolidColor, boolean useLegacy, boolean activityDrawn, int startingWindowType,
- String packageName, int userId) {
+ boolean appPrefersIcon, String packageName, int userId) {
int parameter = 0;
if (newTask) {
parameter |= TYPE_PARAMETER_NEW_TASK;
@@ -134,6 +135,9 @@
UserHandle.of(userId))) {
parameter |= TYPE_PARAMETER_ALLOW_HANDLE_SOLID_COLOR_SCREEN;
}
+ if (appPrefersIcon) {
+ parameter |= TYPE_PARAMETER_APP_PREFERS_ICON;
+ }
return parameter;
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 8f884d2f..f6c431f 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1106,7 +1106,7 @@
return mInsetsSourceProviders;
}
- public DisplayContent getDisplayContent() {
+ public final DisplayContent getDisplayContent() {
return mDisplayContent;
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f14a6f9..34cf436 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1089,6 +1089,7 @@
mClient = c;
mAppOp = appOp;
mToken = token;
+ mDisplayContent = token.mDisplayContent;
mActivityRecord = mToken.asActivityRecord();
mOwnerUid = ownerId;
mShowUserId = showUserId;
@@ -1565,11 +1566,6 @@
}
@Override
- public DisplayContent getDisplayContent() {
- return mToken.getDisplayContent();
- }
-
- @Override
void onDisplayChanged(DisplayContent dc) {
if (dc != null && mDisplayContent != null && dc != mDisplayContent
&& mDisplayContent.getImeInputTarget() == this) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 176bc283..a7d7730 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -86,6 +86,8 @@
namespace android {
+static const bool ENABLE_POINTER_CHOREOGRAPHER = input_flags::enable_pointer_choreographer();
+
// The exponent used to calculate the pointer speed scaling factor.
// The scaling factor is calculated as 2 ^ (speed * exponent),
// where the speed ranges from -7 to + 7 and is supplied by the user.
@@ -327,6 +329,8 @@
TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
bool isInputMethodConnectionActive() override;
+ std::optional<DisplayViewport> getPointerViewportForAssociatedDisplay(
+ int32_t associatedDisplayId) override;
/* --- InputDispatcherPolicyInterface implementation --- */
@@ -374,8 +378,10 @@
virtual PointerIconStyle getCustomPointerIconId();
virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position);
- /* --- PointerControllerPolicyInterface implementation --- */
- std::shared_ptr<PointerControllerInterface> createPointerController() override;
+ /* --- PointerChoreographerPolicyInterface implementation --- */
+ std::shared_ptr<PointerControllerInterface> createPointerController(
+ PointerControllerInterface::ControllerType type) override;
+ void notifyPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position) override;
private:
sp<InputManagerInterface> mInputManager;
@@ -492,7 +498,9 @@
dump += StringPrintf(INDENT "Pointer Capture: %s, seq=%" PRIu32 "\n",
mLocked.pointerCaptureRequest.enable ? "Enabled" : "Disabled",
mLocked.pointerCaptureRequest.seq);
- forEachPointerControllerLocked([&dump](PointerController& pc) { dump += pc.dump(); });
+ if (auto pc = mLocked.legacyPointerController.lock(); pc) {
+ dump += pc->dump();
+ }
} // release lock
dump += "\n";
@@ -537,6 +545,9 @@
[&viewports](PointerController& pc) { pc.onDisplayViewportsUpdated(viewports); });
} // release lock
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mInputManager->getChoreographer().setDisplayViewports(viewports);
+ }
mInputManager->getReader().requestRefreshConfiguration(
InputReaderConfiguration::Change::DISPLAY_INFO);
}
@@ -721,6 +732,7 @@
continue;
}
apply(*pc);
+ it++;
}
}
@@ -735,9 +747,6 @@
if (controller == nullptr) {
ensureSpriteControllerLocked();
- static const bool ENABLE_POINTER_CHOREOGRAPHER =
- input_flags::enable_pointer_choreographer();
-
// Disable the functionality of the legacy PointerController if PointerChoreographer is
// enabled.
controller = PointerController::create(this, mLooper, *mLocked.spriteController,
@@ -749,17 +758,43 @@
return controller;
}
-std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController() {
+std::shared_ptr<PointerControllerInterface> NativeInputManager::createPointerController(
+ PointerControllerInterface::ControllerType type) {
std::scoped_lock _l(mLock);
ensureSpriteControllerLocked();
std::shared_ptr<PointerController> pc =
- PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true);
+ PointerController::create(this, mLooper, *mLocked.spriteController, /*enabled=*/true,
+ type);
mLocked.pointerControllers.emplace_back(pc);
return pc;
}
void NativeInputManager::onPointerDisplayIdChanged(int32_t pointerDisplayId,
const FloatPoint& position) {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ return;
+ }
+ JNIEnv* env = jniEnv();
+ env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
+ position.x, position.y);
+ checkAndClearExceptionFromCallback(env, "onPointerDisplayIdChanged");
+}
+
+void NativeInputManager::notifyPointerDisplayIdChanged(int32_t pointerDisplayId,
+ const FloatPoint& position) {
+ // Notify the Reader so that devices can be reconfigured.
+ { // acquire lock
+ std::scoped_lock _l(mLock);
+ if (mLocked.pointerDisplayId == pointerDisplayId) {
+ return;
+ }
+ mLocked.pointerDisplayId = pointerDisplayId;
+ ALOGI("%s: pointer displayId set to: %d", __func__, pointerDisplayId);
+ } // release lock
+ mInputManager->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+
+ // Notify the system.
JNIEnv* env = jniEnv();
env->CallVoidMethod(mServiceObj, gServiceClassInfo.onPointerDisplayIdChanged, pointerDisplayId,
position.x, position.y);
@@ -1118,19 +1153,23 @@
}
void NativeInputManager::setPointerDisplayId(int32_t displayId) {
- { // acquire lock
- std::scoped_lock _l(mLock);
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ mInputManager->getChoreographer().setDefaultMouseDisplayId(displayId);
+ } else {
+ { // acquire lock
+ std::scoped_lock _l(mLock);
- if (mLocked.pointerDisplayId == displayId) {
- return;
- }
+ if (mLocked.pointerDisplayId == displayId) {
+ return;
+ }
- ALOGI("Setting pointer display id to %d.", displayId);
- mLocked.pointerDisplayId = displayId;
- } // release lock
+ ALOGI("Setting pointer display id to %d.", displayId);
+ mLocked.pointerDisplayId = displayId;
+ } // release lock
- mInputManager->getReader().requestRefreshConfiguration(
- InputReaderConfiguration::Change::DISPLAY_INFO);
+ mInputManager->getReader().requestRefreshConfiguration(
+ InputReaderConfiguration::Change::DISPLAY_INFO);
+ }
}
void NativeInputManager::setPointerSpeed(int32_t speed) {
@@ -1356,6 +1395,11 @@
return result;
}
+std::optional<DisplayViewport> NativeInputManager::getPointerViewportForAssociatedDisplay(
+ int32_t associatedDisplayId) {
+ return mInputManager->getChoreographer().getViewportForPointerDevice(associatedDisplayId);
+}
+
bool NativeInputManager::filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) {
ATRACE_CALL();
JNIEnv* env = jniEnv();
@@ -1689,6 +1733,9 @@
}
FloatPoint NativeInputManager::getMouseCursorPosition() {
+ if (ENABLE_POINTER_CHOREOGRAPHER) {
+ return mInputManager->getChoreographer().getMouseCursorPosition(ADISPLAY_ID_NONE);
+ }
std::scoped_lock _l(mLock);
const auto pc = mLocked.legacyPointerController.lock();
if (!pc) return {AMOTION_EVENT_INVALID_CURSOR_POSITION, AMOTION_EVENT_INVALID_CURSOR_POSITION};
diff --git a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
index 93530cf..acaec21 100644
--- a/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessCheckingService.kt
@@ -16,7 +16,6 @@
package com.android.server.permission.access
-import android.app.admin.DevicePolicyManagerInternal
import android.content.Context
import android.content.pm.PackageManager
import android.content.pm.PackageManagerInternal
@@ -75,7 +74,7 @@
val userIds = MutableIntSet(userManagerService.userIdsIncludingPreCreated)
val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+ val knownPackages = packageManagerInternal.knownPackages
val isLeanback = systemConfig.isLeanback
val configPermissions = systemConfig.permissions
val privilegedPermissionAllowlistPackages =
@@ -152,7 +151,7 @@
isSystemUpdated: Boolean
) {
val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+ val knownPackages = packageManagerInternal.knownPackages
mutateState {
with(policy) {
onStorageVolumeMounted(
@@ -169,7 +168,7 @@
internal fun onPackageAdded(packageName: String) {
val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+ val knownPackages = packageManagerInternal.knownPackages
mutateState {
with(policy) {
onPackageAdded(
@@ -184,7 +183,7 @@
internal fun onPackageRemoved(packageName: String, appId: Int) {
val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+ val knownPackages = packageManagerInternal.knownPackages
mutateState {
with(policy) {
onPackageRemoved(
@@ -200,7 +199,7 @@
internal fun onPackageInstalled(packageName: String, userId: Int) {
val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+ val knownPackages = packageManagerInternal.knownPackages
mutateState {
with(policy) {
onPackageInstalled(
@@ -216,7 +215,7 @@
internal fun onPackageUninstalled(packageName: String, appId: Int, userId: Int) {
val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
+ val knownPackages = packageManagerInternal.knownPackages
mutateState {
with(policy) {
onPackageUninstalled(
@@ -232,69 +231,50 @@
}
internal fun onSystemReady() {
- val (packageStates, disabledSystemPackageStates) = packageManagerLocal.allPackageStates
- val knownPackages = packageManagerInternal.getKnownPackages(packageStates)
- mutateState {
- with(policy) {
- onSystemReady(packageStates, disabledSystemPackageStates, knownPackages)
- }
- }
+ mutateState { with(policy) { onSystemReady() } }
}
private val PackageManagerLocal.allPackageStates:
Pair<Map<String, PackageState>, Map<String, PackageState>>
get() = withUnfilteredSnapshot().use { it.packageStates to it.disabledSystemPackageStates }
- private fun PackageManagerInternal.getKnownPackages(
- packageStates: Map<String, PackageState>
- ): IntMap<Array<String>> =
- MutableIntMap<Array<String>>().apply {
- this[KnownPackages.PACKAGE_INSTALLER] =
- getKnownPackageNames(KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] =
- getKnownPackageNames(
- KnownPackages.PACKAGE_PERMISSION_CONTROLLER,
- UserHandle.USER_SYSTEM
+ private val PackageManagerInternal.knownPackages: IntMap<Array<String>>
+ get() =
+ MutableIntMap<Array<String>>().apply {
+ this[KnownPackages.PACKAGE_INSTALLER] = getKnownPackageNames(
+ KnownPackages.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM
)
- this[KnownPackages.PACKAGE_VERIFIER] =
- getKnownPackageNames(KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_SETUP_WIZARD] =
- getKnownPackageNames(KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] =
- getKnownPackageNames(
- KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
- UserHandle.USER_SYSTEM
+ this[KnownPackages.PACKAGE_PERMISSION_CONTROLLER] = getKnownPackageNames(
+ KnownPackages.PACKAGE_PERMISSION_CONTROLLER, UserHandle.USER_SYSTEM
)
- this[KnownPackages.PACKAGE_CONFIGURATOR] =
- getKnownPackageNames(KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] =
- getKnownPackageNames(
- KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER,
- UserHandle.USER_SYSTEM
+ this[KnownPackages.PACKAGE_VERIFIER] = getKnownPackageNames(
+ KnownPackages.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM
)
- this[KnownPackages.PACKAGE_APP_PREDICTOR] =
- getKnownPackageNames(KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_COMPANION] =
- getKnownPackageNames(KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM)
- this[KnownPackages.PACKAGE_RETAIL_DEMO] =
- getKnownPackageNames(KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM)
- .filter { isProfileOwner(it, packageStates) }
- .toTypedArray()
- this[KnownPackages.PACKAGE_RECENTS] =
- getKnownPackageNames(KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM)
- }
-
- private fun isProfileOwner(
- packageName: String,
- packageStates: Map<String, PackageState>
- ): Boolean {
- val appId = packageStates[packageName]?.appId ?: return false
- val devicePolicyManagerInternal =
- LocalServices.getService(DevicePolicyManagerInternal::class.java) ?: return false
- // TODO(b/169395065): Figure out if this flow makes sense in Device Owner mode.
- return devicePolicyManagerInternal.isActiveProfileOwner(appId) ||
- devicePolicyManagerInternal.isActiveDeviceOwner(appId)
- }
+ this[KnownPackages.PACKAGE_SETUP_WIZARD] = getKnownPackageNames(
+ KnownPackages.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER] = getKnownPackageNames(
+ KnownPackages.PACKAGE_SYSTEM_TEXT_CLASSIFIER, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_CONFIGURATOR] = getKnownPackageNames(
+ KnownPackages.PACKAGE_CONFIGURATOR, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER] = getKnownPackageNames(
+ KnownPackages.PACKAGE_INCIDENT_REPORT_APPROVER, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_APP_PREDICTOR] = getKnownPackageNames(
+ KnownPackages.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_COMPANION] = getKnownPackageNames(
+ KnownPackages.PACKAGE_COMPANION, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_RETAIL_DEMO] = getKnownPackageNames(
+ KnownPackages.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM
+ )
+ this[KnownPackages.PACKAGE_RECENTS] = getKnownPackageNames(
+ KnownPackages.PACKAGE_RECENTS, UserHandle.USER_SYSTEM
+ )
+ }
@OptIn(ExperimentalContracts::class)
internal inline fun <T> getState(action: GetStateScope.() -> T): T {
diff --git a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
index 754f77ec..29fe95c 100644
--- a/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/AccessPolicy.kt
@@ -262,17 +262,8 @@
forEachSchemePolicy { with(it) { onPackageUninstalled(packageName, appId, userId) } }
}
- fun MutateStateScope.onSystemReady(
- packageStates: Map<String, PackageState>,
- disabledSystemPackageStates: Map<String, PackageState>,
- knownPackages: IntMap<Array<String>>
- ) {
- newState.mutateExternalState().apply {
- setPackageStates(packageStates)
- setDisabledSystemPackageStates(disabledSystemPackageStates)
- setKnownPackages(knownPackages)
- setSystemReady(true)
- }
+ fun MutateStateScope.onSystemReady() {
+ newState.mutateExternalState().setSystemReady(true)
forEachSchemePolicy { with(it) { onSystemReady() } }
}
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index 08ba753..010604f 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -1448,15 +1448,6 @@
// Special permissions for the system companion device manager.
return true
}
- if (
- permission.isRetailDemo &&
- packageName in knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!
- ) {
- // Special permission granted only to the OEM specified retail demo app.
- // Note that the original code was passing app ID as UID, so this behavior is kept
- // unchanged.
- return true
- }
if (permission.isRecents && packageName in knownPackages[KnownPackages.PACKAGE_RECENTS]!!) {
// Special permission for the recents app.
return true
@@ -1511,27 +1502,6 @@
}
override fun MutateStateScope.onSystemReady() {
- // HACK: PACKAGE_USAGE_STATS is the only permission with the retailDemo protection flag,
- // and we have to wait until DevicePolicyManagerService is started to know whether the
- // retail demo package is a profile owner so that it can have the permission.
- // Since there's no simple callback for profile owner change, and we are deprecating and
- // removing the retailDemo protection flag in favor of a proper role soon, we can just
- // re-evaluate the permission here, which is also how the old implementation has been
- // working.
- // TODO: Partially revert ag/22690114 once we can remove support for the retailDemo
- // protection flag.
- val externalState = newState.externalState
- for (packageName in externalState.knownPackages[KnownPackages.PACKAGE_RETAIL_DEMO]!!) {
- val appId = externalState.packageStates[packageName]?.appId ?: continue
- newState.userStates.forEachIndexed { _, userId, _ ->
- evaluatePermissionState(
- appId,
- userId,
- Manifest.permission.PACKAGE_USAGE_STATS,
- null
- )
- }
- }
if (!privilegedPermissionAllowlistViolations.isEmpty()) {
throw IllegalStateException(
"Signature|privileged permissions not in privileged" +
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index dd23d9f..e5291d3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -577,8 +577,14 @@
JobStatus jobUIDT = createJobStatus("testGetMaxJobExecutionTimeMs",
createJobInfo(10)
.setUserInitiated(true).setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY));
+ JobStatus jobEj = createJobStatus("testGetMaxJobExecutionTimeMs",
+ createJobInfo(2).setExpedited(true));
+ JobStatus jobReg = createJobStatus("testGetMaxJobExecutionTimeMs",
+ createJobInfo(3));
spyOn(jobUIDT);
when(jobUIDT.shouldTreatAsUserInitiatedJob()).thenReturn(true);
+ spyOn(jobEj);
+ when(jobEj.shouldTreatAsExpeditedJob()).thenReturn(true);
QuotaController quotaController = mService.getQuotaController();
spyOn(quotaController);
@@ -595,6 +601,11 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUIDT));
+
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
+ mService.getMaxJobExecutionTimeMs(jobEj));
+ assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ mService.getMaxJobExecutionTimeMs(jobReg));
}
@Test
@@ -636,7 +647,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -649,7 +660,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -664,7 +675,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -677,7 +688,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -692,7 +703,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -705,7 +716,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -720,7 +731,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -765,7 +776,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -778,7 +789,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -792,7 +803,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -807,7 +818,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -820,7 +831,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
@@ -834,7 +845,7 @@
grantRunUserInitiatedJobsPermission(false);
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobUij));
- assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
mService.getMaxJobExecutionTimeMs(jobEj));
assertEquals(mService.mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS,
mService.getMaxJobExecutionTimeMs(jobReg));
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index ba31944..7b2cd63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -66,8 +66,6 @@
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
import android.view.SurfaceControl;
-import android.view.View;
-import android.view.WindowManager;
import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
@@ -451,17 +449,11 @@
@Test
public void testGetOrientation() {
final DisplayArea.Tokens area = new DisplayArea.Tokens(mWm, ABOVE_TASKS, "test");
- final WindowToken token = createWindowToken(TYPE_APPLICATION_OVERLAY);
- spyOn(token);
- doReturn(mock(DisplayContent.class)).when(token).getDisplayContent();
- doNothing().when(token).setParent(any());
- final WindowState win = createWindowState(token);
- spyOn(win);
- doNothing().when(win).setParent(any());
+ mDisplayContent.addChild(area, POSITION_TOP);
+ final WindowState win = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
win.mAttrs.screenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
- token.addChild(win, 0);
- area.addChild(token);
-
+ win.mToken.reparent(area, POSITION_TOP);
+ spyOn(win);
doReturn(true).when(win).isVisible();
assertEquals("Visible window can request orientation",
@@ -753,13 +745,6 @@
}
}
- private WindowState createWindowState(WindowToken token) {
- return new WindowState(mWm, getTestSession(), new TestIWindow(), token,
- null /* parentWindow */, 0 /* appOp */, new WindowManager.LayoutParams(),
- View.VISIBLE, 0 /* ownerId */, 0 /* showUserId */,
- false /* ownerCanAddInternalSystemWindow */);
- }
-
private WindowToken createWindowToken(int type) {
return new WindowToken.Builder(mWm, new Binder(), type).build();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index a20f26c..f56347f 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -618,4 +618,5 @@
int RIL_UNSOL_TRIGGER_IMS_DEREGISTRATION = 1107;
int RIL_UNSOL_CONNECTION_SETUP_FAILURE = 1108;
int RIL_UNSOL_NOTIFY_ANBR = 1109;
+ int RIL_UNSOL_IMEI_MAPPING_CHANGED = 1110;
}
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index 6bd8595..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index d5d6fb6..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_landscape_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index fe8dc69..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_landscape_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_portrait_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index ccd8a33..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/phone/light_portrait_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index de84c4a..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_landscape_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index af9d7cf..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/dark_portrait_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_landscape_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_landscape_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index 33daab9..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_landscape_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ
diff --git a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_portrait_credential_view_pin_or_password_emergency_call_button.png b/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_portrait_credential_view_pin_or_password_emergency_call_button.png
deleted file mode 100644
index 14a799c..0000000
--- a/vendor/unbundled_google/packages/SystemUIGoogle/tests/screenshotBiometrics/assets/tablet/light_portrait_credential_view_pin_or_password_emergency_call_button.png
+++ /dev/null
Binary files differ